503 lines
12 KiB
Plaintext
503 lines
12 KiB
Plaintext
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
|
|
/*
|
|
* Main authors:
|
|
* Jip J. Dekker <jip.dekker@monash.edu>
|
|
*/
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
%define api.pure
|
|
%define api.header.include {<minizinc/support/mza_parser.tab.hh>}
|
|
|
|
%{
|
|
#include <cstdio>
|
|
#include <list>
|
|
|
|
#include <minizinc/interpreter.hh>
|
|
|
|
//Anonymous struct for when yyparse is exported
|
|
typedef struct MZAContext MZAContext;
|
|
#include <minizinc/support/mza_parser.tab.hh>
|
|
|
|
using namespace MiniZinc;
|
|
|
|
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
|
YY_BUFFER_STATE mza_yy_scan_string ( const char* yy_str );
|
|
|
|
extern int yylex(YYSTYPE*, YYLTYPE*);
|
|
extern FILE* yyin;
|
|
|
|
struct ProcPatch {
|
|
int code;
|
|
BytecodeProc::Mode mode;
|
|
std::vector<std::pair<int, std::pair<std::string, int>>> patch;
|
|
ProcPatch(int code0, BytecodeProc::Mode mode0, std::vector<std::pair<int, std::pair<std::string, int>>> patch0)
|
|
: code(code0), mode(mode0), patch(std::move(patch0)) {}
|
|
};
|
|
|
|
typedef struct MZAContext {
|
|
std::vector<BytecodeProc>& procs;
|
|
std::unordered_map<std::string, int>& proc_map;
|
|
int& max_glob;
|
|
std::vector<ProcPatch> to_patch;
|
|
BytecodeStream proc_body;
|
|
std::unordered_map<std::string, int> labels;
|
|
std::vector<std::pair<int, std::string>> patch_labels;
|
|
std::vector<std::pair<int, std::pair<std::string, int>>> patch_procs;
|
|
} MZAContext;
|
|
|
|
void yyerror(YYLTYPE* location, MZAContext& ctx, const char* s);
|
|
%}
|
|
|
|
%union {
|
|
int iValue;
|
|
char* sValue;
|
|
MiniZinc::BytecodeStream::Instr bValue;
|
|
std::list<int>* liValue;
|
|
std::list<std::string>* sliValue;
|
|
}
|
|
%parse-param {MZAContext& ctx}
|
|
%locations
|
|
%define parse.error verbose
|
|
|
|
%token<iValue> MZA_INT
|
|
%token<iValue> MZA_REG
|
|
%token<iValue> MZA_MODE
|
|
%token<iValue> MZA_CTX
|
|
%token<sValue> MZA_ID
|
|
|
|
%token MZA_COLON ":"
|
|
%token MZA_DELAY "D"
|
|
|
|
%token MZA_GLOBAL "GLOBAL"
|
|
|
|
%token<bValue> MZA_ADDI
|
|
%token<bValue> MZA_SUBI
|
|
%token<bValue> MZA_MULI
|
|
%token<bValue> MZA_DIVI
|
|
%token<bValue> MZA_MODI
|
|
%token<bValue> MZA_INCI
|
|
%token<bValue> MZA_DECI
|
|
|
|
%token<bValue> MZA_IMMI
|
|
%token<bValue> MZA_CLEAR
|
|
%token<bValue> MZA_LOAD_GLOBAL
|
|
%token<bValue> MZA_STORE_GLOBAL
|
|
%token<bValue> MZA_MOV
|
|
|
|
%token<bValue> MZA_JMP
|
|
%token<bValue> MZA_JMPIF
|
|
%token<bValue> MZA_JMPIFNOT
|
|
|
|
%token<bValue> MZA_EQI
|
|
%token<bValue> MZA_LTI
|
|
%token<bValue> MZA_LEI
|
|
|
|
%token<bValue> MZA_AND
|
|
%token<bValue> MZA_OR
|
|
%token<bValue> MZA_NOT
|
|
%token<bValue> MZA_XOR
|
|
|
|
%token<bValue> MZA_ISPAR
|
|
%token<bValue> MZA_ISEMPTY
|
|
%token<bValue> MZA_LENGTH
|
|
%token<bValue> MZA_GET_VEC
|
|
%token<bValue> MZA_GET_ARRAY
|
|
|
|
%token<bValue> MZA_LB
|
|
%token<bValue> MZA_UB
|
|
%token<bValue> MZA_DOM
|
|
|
|
%token<bValue> MZA_MAKE_SET
|
|
%token<bValue> MZA_DIFF
|
|
%token<bValue> MZA_INTERSECTION
|
|
%token<bValue> MZA_UNION
|
|
|
|
%token<bValue> MZA_INTERSECT_DOMAIN
|
|
|
|
%token<bValue> MZA_OPEN_AGGREGATION
|
|
%token<bValue> MZA_CLOSE_AGGREGATION
|
|
%token<bValue> MZA_SIMPLIFY_LIN
|
|
|
|
%token<bValue> MZA_PUSH
|
|
%token<bValue> MZA_POP
|
|
%token<bValue> MZA_POST
|
|
|
|
%token<bValue> MZA_RET
|
|
%token<bValue> MZA_CALL
|
|
%token<bValue> MZA_BUILTIN
|
|
%token<bValue> MZA_TCALL
|
|
|
|
%token<bValue> MZA_ITER_ARRAY
|
|
%token<bValue> MZA_ITER_VEC
|
|
%token<bValue> MZA_ITER_RANGE
|
|
%token<bValue> MZA_ITER_NEXT
|
|
%token<bValue> MZA_ITER_BREAK
|
|
|
|
%token<bValue> MZA_TRACE
|
|
%token<bValue> MZA_ABORT
|
|
|
|
|
|
%type<iValue> delay instruction mode
|
|
%type<bValue> instr instrI instrIR instrR instrRR instrRS instrRRR instrRRS instrRRIRRR instrS
|
|
%type<sValue> label
|
|
%type<liValue> registers
|
|
%type<sliValue> labels
|
|
|
|
%start procedures
|
|
|
|
%%
|
|
|
|
procedures:
|
|
/* empty */
|
|
| procedures procedure
|
|
|
|
procedure:
|
|
":" MZA_ID ":" mode MZA_INT delay instructions
|
|
{
|
|
// Patch jumps with recorded labels
|
|
for (auto& cl : ctx.patch_labels) {
|
|
if (ctx.labels.find(cl.second) == ctx.labels.end()) {
|
|
throw Error("Error: label " + cl.second + " not found\n");
|
|
}
|
|
ctx.proc_body.patchAddress(cl.first, ctx.labels[cl.second]);
|
|
}
|
|
ctx.labels.clear();
|
|
ctx.patch_labels.clear();
|
|
|
|
ctx.proc_body.setNArgs($5);
|
|
|
|
// Add ABORT instruction for error management
|
|
if (ctx.proc_body.size() > 0) {
|
|
ctx.proc_body.addInstr(BytecodeStream::ABORT);
|
|
}
|
|
// Store procedure in the correct place
|
|
auto mode = static_cast<BytecodeProc::Mode>($4);
|
|
auto it = ctx.proc_map.find($2);
|
|
if (it != ctx.proc_map.end()) {
|
|
BytecodeProc& bcp = ctx.procs[it->second];
|
|
if (bcp.mode[mode].size() > 0) {
|
|
throw Error("Error: procedure " + std::string($2) + " already defined before with the same mode\n");
|
|
}
|
|
if (bcp.nargs != $5 ) {
|
|
throw Error("Error: procedure " + std::string($2) + " already defined before with different number of arguments\n");
|
|
}
|
|
bcp.mode[$4] = ctx.proc_body;
|
|
ctx.to_patch.emplace_back(it->second, mode, ctx.patch_procs);
|
|
} else {
|
|
BytecodeProc bcp;
|
|
bcp.name = $2;
|
|
bcp.mode[mode] = ctx.proc_body;
|
|
bcp.nargs = $5;
|
|
bcp.delay = $6;
|
|
ctx.proc_map.emplace($2, ctx.procs.size());
|
|
ctx.to_patch.emplace_back(ctx.procs.size(), mode, ctx.patch_procs);
|
|
ctx.procs.push_back(bcp);
|
|
}
|
|
ctx.proc_body = BytecodeStream();
|
|
ctx.patch_procs.clear();
|
|
}
|
|
|
|
mode:
|
|
/* empty */ { $$ = BytecodeProc::FUN; }
|
|
| MZA_MODE
|
|
|
|
delay:
|
|
/* empty */ { $$ = 0; }
|
|
| MZA_DELAY { $$ = 1; }
|
|
|
|
instructions:
|
|
/* empty */
|
|
| instructions labeled_instr
|
|
|
|
labeled_instr:
|
|
labels instruction
|
|
{
|
|
for (auto l : *$1) {
|
|
ctx.labels.emplace(l, $2);
|
|
}
|
|
delete $1;
|
|
}
|
|
|
|
labels:
|
|
/* empty */ { $$ = new std::list<std::string>(); }
|
|
| labels label { $1->push_back($2); $$ = $1; }
|
|
|
|
label:
|
|
MZA_ID ":"
|
|
|
|
instruction:
|
|
instr
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
}
|
|
| instrI MZA_INT
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addIntVal($2);
|
|
}
|
|
| instrIR MZA_INT MZA_REG
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addIntVal($2);
|
|
ctx.proc_body.addReg($3);
|
|
}
|
|
| instrR MZA_REG
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2);
|
|
}
|
|
| instrRR MZA_REG MZA_REG
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2);
|
|
ctx.proc_body.addReg($3);
|
|
}
|
|
| instrRS MZA_REG MZA_ID
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2);
|
|
ctx.patch_labels.emplace_back(ctx.proc_body.size(), $3);
|
|
ctx.proc_body.addSmallInt(0);
|
|
}
|
|
| instrRRS MZA_REG MZA_REG MZA_ID
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2);
|
|
ctx.proc_body.addReg($3);
|
|
ctx.patch_labels.emplace_back(ctx.proc_body.size(), $4);
|
|
ctx.proc_body.addSmallInt(0);
|
|
}
|
|
| instrRRR MZA_REG MZA_REG MZA_REG
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2);
|
|
ctx.proc_body.addReg($3);
|
|
ctx.proc_body.addReg($4);
|
|
}
|
|
| instrRRIRRR MZA_REG MZA_REG MZA_INT MZA_REG MZA_REG MZA_REG
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2);
|
|
ctx.proc_body.addReg($3);
|
|
ctx.proc_body.addIntVal($4);
|
|
ctx.proc_body.addReg($5);
|
|
ctx.proc_body.addReg($6);
|
|
ctx.proc_body.addReg($7);
|
|
}
|
|
| instrS MZA_ID
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.patch_labels.emplace_back(ctx.proc_body.size(), $2);
|
|
ctx.proc_body.addSmallInt(0);
|
|
}
|
|
| MZA_CALL MZA_MODE MZA_ID MZA_INT registers
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addCharVal($2);
|
|
ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair($3, $5->size()));
|
|
ctx.proc_body.addSmallInt(0);
|
|
ctx.proc_body.addCharVal($4);
|
|
for (auto arg : *$5) {
|
|
ctx.proc_body.addReg(arg);
|
|
}
|
|
delete $5;
|
|
}
|
|
| MZA_BUILTIN MZA_ID registers
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair($2, $3->size()));
|
|
ctx.proc_body.addSmallInt(0);
|
|
for (auto arg : *$3) {
|
|
ctx.proc_body.addReg(arg);
|
|
}
|
|
delete $3;
|
|
}
|
|
| MZA_TCALL MZA_MODE MZA_ID MZA_INT
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addCharVal($2);
|
|
ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair($3, -1));
|
|
ctx.proc_body.addSmallInt(0);
|
|
ctx.proc_body.addCharVal($4);
|
|
}
|
|
| MZA_OPEN_AGGREGATION MZA_CTX
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addCharVal($2);
|
|
}
|
|
// Special cases: Using aggregation context with the same name as instructions
|
|
| MZA_OPEN_AGGREGATION MZA_AND
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addCharVal(AggregationCtx::VCTX_AND);
|
|
}
|
|
| MZA_OPEN_AGGREGATION MZA_OR
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addCharVal(AggregationCtx::VCTX_OR);
|
|
}
|
|
// Special cases: Read INT, but adds Reg value on the Stream
|
|
| MZA_LOAD_GLOBAL MZA_INT MZA_REG
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2, true);
|
|
ctx.max_glob = std::max(ctx.max_glob, $2);
|
|
ctx.proc_body.addReg($3);
|
|
}
|
|
| MZA_STORE_GLOBAL MZA_REG MZA_INT
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addReg($2);
|
|
ctx.proc_body.addReg($3, true);
|
|
ctx.max_glob = std::max(ctx.max_glob, $3);
|
|
}
|
|
| MZA_GET_ARRAY MZA_INT registers
|
|
{
|
|
$$ = ctx.proc_body.size();
|
|
ctx.proc_body.addInstr($1);
|
|
ctx.proc_body.addIntVal($2);
|
|
for (auto arg : *$3) {
|
|
ctx.proc_body.addReg(arg);
|
|
}
|
|
delete $3;
|
|
}
|
|
|
|
instrIR:
|
|
MZA_IMMI
|
|
|
|
instrI:
|
|
MZA_ITER_BREAK
|
|
|
|
instrR:
|
|
MZA_INCI
|
|
| MZA_DECI
|
|
| MZA_PUSH
|
|
| MZA_POP
|
|
| MZA_POST
|
|
| MZA_ITER_NEXT
|
|
| MZA_TRACE
|
|
|
|
instrRR:
|
|
MZA_MOV
|
|
| MZA_NOT
|
|
| MZA_ISPAR
|
|
| MZA_ISEMPTY
|
|
| MZA_LENGTH
|
|
| MZA_MAKE_SET
|
|
| MZA_UB
|
|
| MZA_LB
|
|
| MZA_DOM
|
|
| MZA_CLEAR
|
|
|
|
instrRS:
|
|
MZA_JMPIF
|
|
| MZA_JMPIFNOT
|
|
| MZA_ITER_ARRAY
|
|
| MZA_ITER_VEC
|
|
|
|
instrRRR:
|
|
MZA_ADDI
|
|
| MZA_SUBI
|
|
| MZA_MULI
|
|
| MZA_DIVI
|
|
| MZA_MODI
|
|
| MZA_EQI
|
|
| MZA_LTI
|
|
| MZA_LEI
|
|
| MZA_AND
|
|
| MZA_OR
|
|
| MZA_XOR
|
|
| MZA_GET_VEC
|
|
| MZA_DIFF
|
|
| MZA_INTERSECTION
|
|
| MZA_UNION
|
|
| MZA_INTERSECT_DOMAIN
|
|
|
|
instrRRS:
|
|
MZA_ITER_RANGE
|
|
|
|
instrRRIRRR:
|
|
MZA_SIMPLIFY_LIN
|
|
|
|
instrS:
|
|
MZA_JMP
|
|
|
|
instr:
|
|
MZA_RET
|
|
| MZA_CLOSE_AGGREGATION
|
|
| MZA_ABORT
|
|
|
|
|
|
registers:
|
|
/* empty */ { $$ = new std::list<int>(); }
|
|
| registers MZA_REG { $1->push_back($2); $$ = $1; }
|
|
|
|
%%
|
|
|
|
#include <minizinc/interpreter/primitives.hh>
|
|
#include <minizinc/interpreter.hh>
|
|
|
|
void yyerror(YYLTYPE* location, MZAContext& ctx, const char* s) {
|
|
std::ostringstream oss;
|
|
oss << "Cannot parse MiniZinc assembly in line " << location->first_line << ": " << std::string(s);
|
|
throw Error(oss.str());
|
|
}
|
|
|
|
std::pair<int, std::vector<BytecodeProc>> parse_mza(const std::string& assembly_str) {
|
|
std::vector<BytecodeProc> procs;
|
|
std::unordered_map<std::string, int> proc_map;
|
|
int max_glob;
|
|
// Initialise first slots with
|
|
for (PrimitiveMap::Primitive* p : primitiveMap()) {
|
|
BytecodeProc bcp;
|
|
bcp.name = p->name();
|
|
bcp.nargs = p->n_args();
|
|
bcp.delay = false;
|
|
procs.push_back(bcp);
|
|
proc_map.emplace(bcp.name, p->ident());
|
|
}
|
|
mza_yy_scan_string(assembly_str.c_str());
|
|
MZAContext ctx = MZAContext{procs, proc_map, max_glob};
|
|
int err = yyparse(ctx);
|
|
if (err != 0) {
|
|
throw std::runtime_error("Cannot parse MiniZinc assembly: " + std::to_string(err));
|
|
}
|
|
for (auto& p : ctx.to_patch) {
|
|
int code = p.code;
|
|
BytecodeProc::Mode mode = p.mode;
|
|
for (auto& patch : p.patch) {
|
|
int& nargs = patch.second.second;
|
|
std::string& name = patch.second.first;
|
|
|
|
if (nargs >= 0 && nargs != procs[proc_map[name]].nargs) {
|
|
throw Error("Error: number of arguments in call to " + patch.second.first + " at position "
|
|
+ std::to_string(patch.second.second) + " has an invalid number of arguments.\n");
|
|
}
|
|
procs[code].mode[mode].patchAddress(patch.first, proc_map[patch.second.first]);
|
|
}
|
|
}
|
|
return {max_glob, procs};
|
|
}
|