diff --git a/coco/Parser.cpp b/coco/Parser.cpp index fe5a29f..61fc797 100644 --- a/coco/Parser.cpp +++ b/coco/Parser.cpp @@ -1,664 +1,654 @@ /*---------------------------------------------------------------------- Compiler Generator Coco/R, Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz extended by M. Loeberbauer & A. Woess, Univ. of Linz ported to C++ by Csaba Balazs, University of Szeged with improvements by Pat Terry, Rhodes University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. As an exception, it is allowed to write an extension of Coco/R that is used as a plugin in non-free software. If not otherwise stated, any source code generated by Coco/R (other than Coco/R itself) does not fall under the GNU General Public License. -----------------------------------------------------------------------*/ #include #include "Parser.h" #include "Scanner.h" void Parser::SynErr(int n) { if (errDist >= minErrDist) errors->SynErr(la->line, la->col, n); errDist = 0; } void Parser::SemErr(const wchar_t* msg) { if (errDist >= minErrDist) errors->Error(t->line, t->col, msg); errDist = 0; } void Parser::Get() { for (;;) { t = la; la = scanner->Scan(); if (la->kind <= maxT) { ++errDist; break; } if (dummyToken != t) { dummyToken->kind = t->kind; dummyToken->pos = t->pos; dummyToken->col = t->col; dummyToken->line = t->line; dummyToken->next = NULL; coco_string_delete(dummyToken->val); dummyToken->val = coco_string_create(t->val); t = dummyToken; } la = t; } } void Parser::Expect(int n) { if (la->kind==n) Get(); else { SynErr(n); } } void Parser::ExpectWeak(int n, int follow) { if (la->kind == n) Get(); else { SynErr(n); while (!StartOf(follow)) Get(); } } bool Parser::WeakSeparator(int n, int syFol, int repFol) { if (la->kind == n) {Get(); return true;} else if (StartOf(repFol)) {return false;} else { SynErr(n); while (!(StartOf(syFol) || StartOf(repFol) || StartOf(0))) { Get(); } return StartOf(syFol); } } void Parser::Xreate() { while (la->kind == _ident || la->kind == 24 /* "rule" */) { if (la->kind == _ident) { FDecl(); } else { RuleDecl(); } } } void Parser::FDecl() { std::wstring fname; Expression fbody; std::wstring varname; TypeAnnotation typIn; TypeAnnotation typOut; Ident(fname); Expect(6 /* "=" */); Expect(7 /* "function" */); Expect(8 /* ":" */); Function f = Function(fname); if (StartOf(1)) { Type(typOut); f.setReturnType(typOut); } else if (la->kind == _lparen) { Get(); Ident(varname); Expect(8 /* ":" */); Type(typIn); f.addArg(varname, typIn); while (la->kind == 9 /* "," */) { Get(); Ident(varname); Expect(8 /* ":" */); Type(typIn); f.addArg(varname, typIn); } Expect(_rparen); - Expect(10 /* "->" */); + Expect(10 /* "-" */); + Expect(11 /* ">" */); Type(typOut); f.setReturnType(typOut); while (la->kind == 9 /* "," */) { Get(); FnTag(f); } - } else SynErr(35); - Expect(11 /* "{" */); + } else SynErr(34); + Expect(12 /* "{" */); while (la->kind == _ident) { VDecl(f); - Expect(12 /* ";" */); + Expect(13 /* ";" */); } Expr(fbody); - Expect(12 /* ";" */); - Expect(13 /* "}" */); + Expect(13 /* ";" */); + Expect(14 /* "}" */); f.setBody(fbody); root.add(f); } void Parser::RuleDecl() { Expect(24 /* "rule" */); Expect(8 /* ":" */); - RuleArguments args; RuleGuards guards; RuleBody body; DomainAnnotation typ; + RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; Expect(_lparen); Ident(arg); Expect(8 /* ":" */); Domain(typ); args.add(arg, typ); while (la->kind == 9 /* "," */) { Get(); Ident(arg); Expect(8 /* ":" */); Domain(typ); args.add(arg, typ); } Expect(_rparen); if (la->kind == 25 /* "case" */) { Get(); RGuard(guards); while (la->kind == 9 /* "," */) { Get(); RGuard(guards); } } - Expect(11 /* "{" */); + Expect(12 /* "{" */); RBody(args, guards); - Expect(13 /* "}" */); + Expect(14 /* "}" */); } void Parser::Ident(std::wstring& name) { Expect(_ident); name = t->val; } void Parser::Type(TypeAnnotation& typ) { TypeAnnotation typ2; TypeAtom typ3; - if (la->kind == 19 /* "[" */) { + if (la->kind == 20 /* "[" */) { Get(); Type(typ2); - Expect(20 /* "]" */); + Expect(21 /* "]" */); typ = TypeAnnotation(TypeOperator::LIST, typ2); } else if (StartOf(2)) { TypeTerm(typ3); typ = TypeAnnotation(typ3); - } else SynErr(36); + } else SynErr(35); } void Parser::FnTag(Function& f) { std::wstring tag; TagModifier mod = TagModifier::NONE; Ident(tag); - if (la->kind == 21 /* "-" */) { + if (la->kind == 10 /* "-" */) { Get(); TagMod(mod); } f.addTag(tag, mod); } void Parser::VDecl(Function& f) { std::wstring vname; Expression e; TypeAnnotation typ; Ident(vname); Expect(6 /* "=" */); if (StartOf(3)) { Expr(e); Expect(8 /* ":" */); Type(typ); f.addDeclaration(vname, typ, e); - } else if (la->kind == 19 /* "[" */) { + } else if (la->kind == 20 /* "[" */) { ListLiteral(e); Expect(8 /* ":" */); Type(typ); f.addListDeclaration(vname, typ, e); - } else SynErr(37); + } else SynErr(36); } void Parser::Expr(Expression& e) { Operator op; Expression e2; SimExpr(e); - if (la->kind == 6 /* "=" */ || la->kind == 29 /* ">" */ || la->kind == 33 /* "<" */) { + if (la->kind == 6 /* "=" */ || la->kind == 11 /* ">" */ || la->kind == 32 /* "<" */) { RelOp(op); SimExpr(e2); - e = Expression(op, e); e.addArg(e2); + e = Expression(op, {e, e2}); } } void Parser::TypeTerm(TypeAtom& typ) { - if (la->kind == 14 /* "string" */) { + if (la->kind == 15 /* "string" */) { Get(); - } else if (la->kind == 15 /* "int" */) { + } else if (la->kind == 16 /* "int" */) { Get(); - } else if (la->kind == 16 /* "num" */) { + } else if (la->kind == 17 /* "num" */) { Get(); - } else if (la->kind == 17 /* "float" */) { + } else if (la->kind == 18 /* "float" */) { Get(); - } else if (la->kind == 18 /* "bool" */) { + } else if (la->kind == 19 /* "bool" */) { Get(); - } else SynErr(38); + } else SynErr(37); typ = Atom(t->val); } void Parser::ListLiteral(Expression& e) { Expression e2; - Expect(19 /* "[" */); - e = Expression(Operator::LIST, Expression()); + Expect(20 /* "[" */); + e.setOp(Operator::LIST); if (StartOf(3)) { Expr(e2); - e.addArg(e2); + e.addArg(std::move(e2)); while (la->kind == 9 /* "," */) { Get(); Expr(e2); - e.addArg(e2); + e.addArg(std::move(e2)); } } - Expect(20 /* "]" */); + Expect(21 /* "]" */); } void Parser::TagMod(TagModifier& mod) { if (la->kind == 22 /* "assert" */) { Get(); mod = TagModifier::ASSERT; } else if (la->kind == 23 /* "require" */) { Get(); mod = TagModifier::REQUIRE; - } else SynErr(39); + } else SynErr(38); } void Parser::Domain(DomainAnnotation& dom) { if (la->kind == 7 /* "function" */) { Get(); dom = DomainAnnotation::FUNCTION; } else if (la->kind == 26 /* "variable" */) { Get(); dom = DomainAnnotation::VARIABLE; - } else SynErr(40); + } else SynErr(39); } void Parser::RGuard(RuleGuards& guards) { - MetaExpression e; + Expression e; MetaExpr(e); - guards.add(e); + guards.add(std::move(e)); } -void Parser::RBody(RuleBody& body) { - MetaExpr e; +void Parser::RBody(const RuleArguments& args, const RuleGuards& guards) { + Expression e; std::wstring msg; Expect(27 /* "warning" */); MetaExpr(e); if (la->kind == 28 /* "message" */) { Get(); Expect(_string); + msg = t->val; } + root.add(new RuleWarning(RuleArguments(args), RuleGuards(guards), std::move(e), Atom(msg))); } -void Parser::MetaExpr(MetaExpression& e) { +void Parser::MetaExpr(Expression& e) { + Operator op; Expression e2; MetaExpr2(e); - if (la->kind == 21 /* "-" */) { + if (la->kind == 10 /* "-" */) { MetaOp(op); MetaExpr2(e2); - MetaParameters params; - params.add(e); - params.add(e2); - e = MetaExpression(op, params); + e = Expression(op, {e, e2}); } } -void Parser::MetaExpr2(MetaExpression& e) { - std::wstring i1, i2, infix; MetaParameters params; +void Parser::MetaExpr2(Expression& e) { + std::wstring i1, i2, infix; if (checkParametersList()) { Ident(i1); + e = Expression(Operator::CALL, {Expression(Atom(i1))}); Expect(_lparen); - if (la->kind == _ident || la->kind == _lparen) { - MetaParams(params); + if (StartOf(3)) { + CalleeParams(e); } Expect(_rparen); - e = MetaExpression(Operator::CALL, i1, params); } else if (checkInfix()) { Ident(i1); Ident(infix); Ident(i2); - params.add(Expression(Atom(i1->val))); - params.add(Expression(Atom(i2->val))); - e = MetaExpression(Operator::CALL, Atom(infix->val), params); + e = Expression(Operator::CALL, {Expression(Atom(infix))}); + e.addArg(Expression(Atom(i1))); + e.addArg(Expression(Atom(i2))); + } else if (la->kind == _ident) { Ident(i1); - e = MetaExpression(Atom(i1->val)); + e = Expression(Atom(i1)); } else if (la->kind == _lparen) { Get(); MetaExpr(e); Expect(_rparen); - } else SynErr(41); + } else SynErr(40); } void Parser::MetaOp(Operator& op) { - Expect(21 /* "-" */); - Expect(29 /* ">" */); - op = OPERATOR::IMPL; + Expect(10 /* "-" */); + Expect(11 /* ">" */); + op = Operator::IMPL; } -void Parser::MetaParams(MetaParameters& params) { - MetaExpr e; - MetaExpr(e); - params.add(e); +void Parser::CalleeParams(Expression& e) { + Expression e2; + Expr(e2); + e.addArg(std::move(e2)); while (la->kind == 9 /* "," */) { Get(); - MetaExpr(e); - params.add(e) + Expr(e2); + e.addArg(std::move(e2)); } } void Parser::SimExpr(Expression& e) { Operator op; Expression e2; Term(e); - while (la->kind == 21 /* "-" */ || la->kind == 30 /* "+" */) { + while (la->kind == 10 /* "-" */ || la->kind == 29 /* "+" */) { AddOp(op); Term(e2); - e = Expression(op, e); e.addArg(e2); + e = Expression(op, {e, e2}); } } void Parser::RelOp(Operator& op) { op = Operator::EQU; if (la->kind == 6 /* "=" */) { Get(); Expect(6 /* "=" */); - } else if (la->kind == 33 /* "<" */) { + } else if (la->kind == 32 /* "<" */) { Get(); op = Operator::LSS; - } else if (la->kind == 29 /* ">" */) { + } else if (la->kind == 11 /* ">" */) { Get(); op = Operator::GTR; - } else SynErr(42); + } else SynErr(41); } void Parser::Term(Expression& e) { Operator op; Expression e2; Factor(e); - while (la->kind == 31 /* "*" */ || la->kind == 32 /* "/" */) { + while (la->kind == 30 /* "*" */ || la->kind == 31 /* "/" */) { MulOp(op); Factor(e2); - e = Expression(op, e); e.addArg(e2); + e = Expression(op, {e, e2}); } } void Parser::AddOp(Operator& op) { op = Operator::ADD; - if (la->kind == 30 /* "+" */) { + if (la->kind == 29 /* "+" */) { Get(); - } else if (la->kind == 21 /* "-" */) { + } else if (la->kind == 10 /* "-" */) { Get(); op = Operator::SUB; - } else SynErr(43); + } else SynErr(42); } void Parser::Factor(Expression& e) { std::wstring name; if (checkParametersList()) { Ident(name); - e = Expression(Operator::CALL, Atom(name)); + e = Expression(Operator::CALL, {Atom(name)}); Expect(_lparen); if (StartOf(3)) { CalleeParams(e); } Expect(_rparen); } else if (la->kind == _ident) { Ident(name); e = Expression(Atom(name)); } else if (la->kind == _number) { Get(); e = Expression(Atom(t->val)); - } else if (la->kind == 21 /* "-" */) { + } else if (la->kind == 10 /* "-" */) { Get(); Factor(e); - e = Expression(Operator::NEG, e); + e = Expression(Operator::NEG, {e}); } else if (la->kind == _lparen) { Get(); Expr(e); Expect(_rparen); - } else SynErr(44); + } else SynErr(43); } void Parser::MulOp(Operator& op) { op = Operator::MUL; - if (la->kind == 31 /* "*" */) { + if (la->kind == 30 /* "*" */) { Get(); - } else if (la->kind == 32 /* "/" */) { + } else if (la->kind == 31 /* "/" */) { Get(); op = Operator::DIV; - } else SynErr(45); -} - -void Parser::CalleeParams(Expression& e) { - Expression e2; - Expr(e2); - e.addArg(e2); - while (la->kind == 9 /* "," */) { - Get(); - Expr(e2); - e.addArg(e2); - } + } else SynErr(44); } // If the user declared a method Init and a mehtod Destroy they should // be called in the contructur and the destructor respctively. // // The following templates are used to recognize if the user declared // the methods Init and Destroy. template struct ParserInitExistsRecognizer { template struct ExistsIfInitIsDefinedMarker{}; struct InitIsMissingType { char dummy1; }; struct InitExistsType { char dummy1; char dummy2; }; // exists always template static InitIsMissingType is_here(...); // exist only if ExistsIfInitIsDefinedMarker is defined template static InitExistsType is_here(ExistsIfInitIsDefinedMarker*); enum { InitExists = (sizeof(is_here(NULL)) == sizeof(InitExistsType)) }; }; template struct ParserDestroyExistsRecognizer { template struct ExistsIfDestroyIsDefinedMarker{}; struct DestroyIsMissingType { char dummy1; }; struct DestroyExistsType { char dummy1; char dummy2; }; // exists always template static DestroyIsMissingType is_here(...); // exist only if ExistsIfDestroyIsDefinedMarker is defined template static DestroyExistsType is_here(ExistsIfDestroyIsDefinedMarker*); enum { DestroyExists = (sizeof(is_here(NULL)) == sizeof(DestroyExistsType)) }; }; // The folloing templates are used to call the Init and Destroy methods if they exist. // Generic case of the ParserInitCaller, gets used if the Init method is missing template::InitExists> struct ParserInitCaller { static void CallInit(T *t) { // nothing to do } }; // True case of the ParserInitCaller, gets used if the Init method exists template struct ParserInitCaller { static void CallInit(T *t) { t->Init(); } }; // Generic case of the ParserDestroyCaller, gets used if the Destroy method is missing template::DestroyExists> struct ParserDestroyCaller { static void CallDestroy(T *t) { // nothing to do } }; // True case of the ParserDestroyCaller, gets used if the Destroy method exists template struct ParserDestroyCaller { static void CallDestroy(T *t) { t->Destroy(); } }; void Parser::Parse() { t = NULL; la = dummyToken = new Token(); la->val = coco_string_create(L"Dummy Token"); Get(); Xreate(); Expect(0); } Parser::Parser(Scanner *scanner) { - maxT = 34; + maxT = 33; ParserInitCaller::CallInit(this); dummyToken = NULL; t = la = NULL; minErrDist = 2; errDist = minErrDist; this->scanner = scanner; errors = new Errors(); } bool Parser::StartOf(int s) { const bool T = true; const bool x = false; - static bool set[4][36] = { - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x} + static bool set[4][35] = { + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,T,T,x, T,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x} }; return set[s][la->kind]; } Parser::~Parser() { ParserDestroyCaller::CallDestroy(this); delete errors; delete dummyToken; } Errors::Errors() { count = 0; } void Errors::SynErr(int line, int col, int n) { wchar_t* s; switch (n) { case 0: s = coco_string_create(L"EOF expected"); break; case 1: s = coco_string_create(L"ident expected"); break; case 2: s = coco_string_create(L"number expected"); break; case 3: s = coco_string_create(L"string expected"); break; case 4: s = coco_string_create(L"lparen expected"); break; case 5: s = coco_string_create(L"rparen expected"); break; case 6: s = coco_string_create(L"\"=\" expected"); break; case 7: s = coco_string_create(L"\"function\" expected"); break; case 8: s = coco_string_create(L"\":\" expected"); break; case 9: s = coco_string_create(L"\",\" expected"); break; - case 10: s = coco_string_create(L"\"->\" expected"); break; - case 11: s = coco_string_create(L"\"{\" expected"); break; - case 12: s = coco_string_create(L"\";\" expected"); break; - case 13: s = coco_string_create(L"\"}\" expected"); break; - case 14: s = coco_string_create(L"\"string\" expected"); break; - case 15: s = coco_string_create(L"\"int\" expected"); break; - case 16: s = coco_string_create(L"\"num\" expected"); break; - case 17: s = coco_string_create(L"\"float\" expected"); break; - case 18: s = coco_string_create(L"\"bool\" expected"); break; - case 19: s = coco_string_create(L"\"[\" expected"); break; - case 20: s = coco_string_create(L"\"]\" expected"); break; - case 21: s = coco_string_create(L"\"-\" expected"); break; + case 10: s = coco_string_create(L"\"-\" expected"); break; + case 11: s = coco_string_create(L"\">\" expected"); break; + case 12: s = coco_string_create(L"\"{\" expected"); break; + case 13: s = coco_string_create(L"\";\" expected"); break; + case 14: s = coco_string_create(L"\"}\" expected"); break; + case 15: s = coco_string_create(L"\"string\" expected"); break; + case 16: s = coco_string_create(L"\"int\" expected"); break; + case 17: s = coco_string_create(L"\"num\" expected"); break; + case 18: s = coco_string_create(L"\"float\" expected"); break; + case 19: s = coco_string_create(L"\"bool\" expected"); break; + case 20: s = coco_string_create(L"\"[\" expected"); break; + case 21: s = coco_string_create(L"\"]\" expected"); break; case 22: s = coco_string_create(L"\"assert\" expected"); break; case 23: s = coco_string_create(L"\"require\" expected"); break; case 24: s = coco_string_create(L"\"rule\" expected"); break; case 25: s = coco_string_create(L"\"case\" expected"); break; case 26: s = coco_string_create(L"\"variable\" expected"); break; case 27: s = coco_string_create(L"\"warning\" expected"); break; case 28: s = coco_string_create(L"\"message\" expected"); break; - case 29: s = coco_string_create(L"\">\" expected"); break; - case 30: s = coco_string_create(L"\"+\" expected"); break; - case 31: s = coco_string_create(L"\"*\" expected"); break; - case 32: s = coco_string_create(L"\"/\" expected"); break; - case 33: s = coco_string_create(L"\"<\" expected"); break; - case 34: s = coco_string_create(L"??? expected"); break; - case 35: s = coco_string_create(L"invalid FDecl"); break; - case 36: s = coco_string_create(L"invalid Type"); break; - case 37: s = coco_string_create(L"invalid VDecl"); break; - case 38: s = coco_string_create(L"invalid TypeTerm"); break; - case 39: s = coco_string_create(L"invalid TagMod"); break; - case 40: s = coco_string_create(L"invalid Domain"); break; - case 41: s = coco_string_create(L"invalid MetaExpr2"); break; - case 42: s = coco_string_create(L"invalid RelOp"); break; - case 43: s = coco_string_create(L"invalid AddOp"); break; - case 44: s = coco_string_create(L"invalid Factor"); break; - case 45: s = coco_string_create(L"invalid MulOp"); break; + case 29: s = coco_string_create(L"\"+\" expected"); break; + case 30: s = coco_string_create(L"\"*\" expected"); break; + case 31: s = coco_string_create(L"\"/\" expected"); break; + case 32: s = coco_string_create(L"\"<\" expected"); break; + case 33: s = coco_string_create(L"??? expected"); break; + case 34: s = coco_string_create(L"invalid FDecl"); break; + case 35: s = coco_string_create(L"invalid Type"); break; + case 36: s = coco_string_create(L"invalid VDecl"); break; + case 37: s = coco_string_create(L"invalid TypeTerm"); break; + case 38: s = coco_string_create(L"invalid TagMod"); break; + case 39: s = coco_string_create(L"invalid Domain"); break; + case 40: s = coco_string_create(L"invalid MetaExpr2"); break; + case 41: s = coco_string_create(L"invalid RelOp"); break; + case 42: s = coco_string_create(L"invalid AddOp"); break; + case 43: s = coco_string_create(L"invalid Factor"); break; + case 44: s = coco_string_create(L"invalid MulOp"); break; default: { wchar_t format[20]; coco_swprintf(format, 20, L"error %d", n); s = coco_string_create(format); } break; } wprintf(L"-- line %d col %d: %ls\n", line, col, s); coco_string_delete(s); count++; } void Errors::Error(int line, int col, const wchar_t *s) { wprintf(L"-- line %d col %d: %ls\n", line, col, s); count++; } void Errors::Warning(int line, int col, const wchar_t *s) { wprintf(L"-- line %d col %d: %ls\n", line, col, s); } void Errors::Warning(const wchar_t *s) { wprintf(L"%ls\n", s); } void Errors::Exception(const wchar_t* s) { wprintf(L"%ls", s); exit(1); } diff --git a/coco/Parser.h b/coco/Parser.h index 8501f6d..b986250 100644 --- a/coco/Parser.h +++ b/coco/Parser.h @@ -1,141 +1,138 @@ /*---------------------------------------------------------------------- Compiler Generator Coco/R, Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz extended by M. Loeberbauer & A. Woess, Univ. of Linz ported to C++ by Csaba Balazs, University of Szeged with improvements by Pat Terry, Rhodes University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. As an exception, it is allowed to write an extension of Coco/R that is used as a plugin in non-free software. If not otherwise stated, any source code generated by Coco/R (other than Coco/R itself) does not fall under the GNU General Public License. -----------------------------------------------------------------------*/ #if !defined(COCO_PARSER_H__) #define COCO_PARSER_H__ #include "ast.h" #include #include "Scanner.h" class Errors { public: int count; // number of errors detected Errors(); void SynErr(int line, int col, int n); void Error(int line, int col, const wchar_t *s); void Warning(int line, int col, const wchar_t *s); void Warning(const wchar_t *s); void Exception(const wchar_t *s); }; // Errors class Parser { private: enum { _EOF=0, _ident=1, _number=2, _string=3, _lparen=4, _rparen=5 }; int maxT; Token *dummyToken; int errDist; int minErrDist; void SynErr(int n); void Get(); void Expect(int n); bool StartOf(int s); void ExpectWeak(int n, int follow); bool WeakSeparator(int n, int syFol, int repFol); public: Scanner *scanner; Errors *errors; Token *t; // last recognized token Token *la; // lookahead token AST root; // current program unit (procedure or main program) bool checkParametersList() { - scanner.ResetPeek(); - Token x = scanner->Peek(); + scanner->ResetPeek(); + Token* x = scanner->Peek(); return la->kind == _ident && x->kind == _lparen; } bool checkInfix() { - scanner.ResetPeek(); - Token x = scanner->Peek(); + scanner->ResetPeek(); + Token* x = scanner->Peek(); return la->kind == _ident && x->kind == _ident; } -bool check - Parser(Scanner *scanner); ~Parser(); void SemErr(const wchar_t* msg); void Xreate(); void FDecl(); void RuleDecl(); void Ident(std::wstring& name); void Type(TypeAnnotation& typ); void FnTag(Function& f); void VDecl(Function& f); void Expr(Expression& e); void TypeTerm(TypeAtom& typ); void ListLiteral(Expression& e); void TagMod(TagModifier& mod); void Domain(DomainAnnotation& dom); void RGuard(RuleGuards& guards); - void RBody(RuleBody& body); - void MetaExpr(MetaExpression& e); - void MetaExpr2(MetaExpression& e); + void RBody(const RuleArguments& args, const RuleGuards& guards); + void MetaExpr(Expression& e); + void MetaExpr2(Expression& e); void MetaOp(Operator& op); - void MetaParams(MetaParameters& params); + void CalleeParams(Expression& e); void SimExpr(Expression& e); void RelOp(Operator& op); void Term(Expression& e); void AddOp(Operator& op); void Factor(Expression& e); void MulOp(Operator& op); - void CalleeParams(Expression& e); void Parse(); }; // end Parser #endif diff --git a/coco/Scanner.cpp b/coco/Scanner.cpp index f19591f..78e2975 100644 --- a/coco/Scanner.cpp +++ b/coco/Scanner.cpp @@ -1,764 +1,759 @@ /*---------------------------------------------------------------------- Compiler Generator Coco/R, Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz extended by M. Loeberbauer & A. Woess, Univ. of Linz ported to C++ by Csaba Balazs, University of Szeged with improvements by Pat Terry, Rhodes University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. As an exception, it is allowed to write an extension of Coco/R that is used as a plugin in non-free software. If not otherwise stated, any source code generated by Coco/R (other than Coco/R itself) does not fall under the GNU General Public License. -----------------------------------------------------------------------*/ #include #include #include "Scanner.h" // string handling, wide character wchar_t* coco_string_create(const wchar_t* value) { return coco_string_create(value, 0); } wchar_t* coco_string_create(const wchar_t *value, int startIndex) { int valueLen = 0; int len = 0; if (value) { valueLen = wcslen(value); len = valueLen - startIndex; } return coco_string_create(value, startIndex, len); } wchar_t* coco_string_create(const wchar_t *value, int startIndex, int length) { int len = 0; wchar_t* data; if (value) { len = length; } data = new wchar_t[len + 1]; wcsncpy(data, &(value[startIndex]), len); data[len] = 0; return data; } wchar_t* coco_string_create_upper(const wchar_t* data) { if (!data) { return NULL; } int dataLen = 0; if (data) { dataLen = wcslen(data); } wchar_t *newData = new wchar_t[dataLen + 1]; for (int i = 0; i <= dataLen; i++) { if ((L'a' <= data[i]) && (data[i] <= L'z')) { newData[i] = data[i] + (L'A' - L'a'); } else { newData[i] = data[i]; } } newData[dataLen] = L'\0'; return newData; } wchar_t* coco_string_create_lower(const wchar_t* data) { if (!data) { return NULL; } int dataLen = wcslen(data); return coco_string_create_lower(data, 0, dataLen); } wchar_t* coco_string_create_lower(const wchar_t* data, int startIndex, int dataLen) { if (!data) { return NULL; } wchar_t* newData = new wchar_t[dataLen + 1]; for (int i = 0; i <= dataLen; i++) { wchar_t ch = data[startIndex + i]; if ((L'A' <= ch) && (ch <= L'Z')) { newData[i] = ch - (L'A' - L'a'); } else { newData[i] = ch; } } newData[dataLen] = L'\0'; return newData; } wchar_t* coco_string_create_append(const wchar_t* data1, const wchar_t* data2) { wchar_t* data; int data1Len = 0; int data2Len = 0; if (data1) { data1Len = wcslen(data1); } if (data2) {data2Len = wcslen(data2); } data = new wchar_t[data1Len + data2Len + 1]; if (data1) { wcscpy(data, data1); } if (data2) { wcscpy(data + data1Len, data2); } data[data1Len + data2Len] = 0; return data; } wchar_t* coco_string_create_append(const wchar_t *target, const wchar_t appendix) { int targetLen = coco_string_length(target); wchar_t* data = new wchar_t[targetLen + 2]; wcsncpy(data, target, targetLen); data[targetLen] = appendix; data[targetLen + 1] = 0; return data; } void coco_string_delete(wchar_t* &data) { delete [] data; data = NULL; } int coco_string_length(const wchar_t* data) { if (data) { return wcslen(data); } return 0; } bool coco_string_endswith(const wchar_t* data, const wchar_t *end) { int dataLen = wcslen(data); int endLen = wcslen(end); return (endLen <= dataLen) && (wcscmp(data + dataLen - endLen, end) == 0); } int coco_string_indexof(const wchar_t* data, const wchar_t value) { const wchar_t* chr = wcschr(data, value); if (chr) { return (chr-data); } return -1; } int coco_string_lastindexof(const wchar_t* data, const wchar_t value) { const wchar_t* chr = wcsrchr(data, value); if (chr) { return (chr-data); } return -1; } void coco_string_merge(wchar_t* &target, const wchar_t* appendix) { if (!appendix) { return; } wchar_t* data = coco_string_create_append(target, appendix); delete [] target; target = data; } bool coco_string_equal(const wchar_t* data1, const wchar_t* data2) { return wcscmp( data1, data2 ) == 0; } int coco_string_compareto(const wchar_t* data1, const wchar_t* data2) { return wcscmp(data1, data2); } int coco_string_hash(const wchar_t *data) { int h = 0; if (!data) { return 0; } while (*data != 0) { h = (h * 7) ^ *data; ++data; } if (h < 0) { h = -h; } return h; } // string handling, ascii character wchar_t* coco_string_create(const char* value) { int len = 0; if (value) { len = strlen(value); } wchar_t* data = new wchar_t[len + 1]; for (int i = 0; i < len; ++i) { data[i] = (wchar_t) value[i]; } data[len] = 0; return data; } char* coco_string_create_char(const wchar_t *value) { int len = coco_string_length(value); char *res = new char[len + 1]; for (int i = 0; i < len; ++i) { res[i] = (char) value[i]; } res[len] = 0; return res; } void coco_string_delete(char* &data) { delete [] data; data = NULL; } Token::Token() { kind = 0; pos = 0; col = 0; line = 0; val = NULL; next = NULL; } Token::~Token() { coco_string_delete(val); } Buffer::Buffer(FILE* s, bool isUserStream) { // ensure binary read on windows #if _MSC_VER >= 1300 _setmode(_fileno(s), _O_BINARY); #endif stream = s; this->isUserStream = isUserStream; if (CanSeek()) { fseek(s, 0, SEEK_END); fileLen = ftell(s); fseek(s, 0, SEEK_SET); bufLen = (fileLen < COCO_MAX_BUFFER_LENGTH) ? fileLen : COCO_MAX_BUFFER_LENGTH; bufStart = INT_MAX; // nothing in the buffer so far } else { fileLen = bufLen = bufStart = 0; } bufCapacity = (bufLen>0) ? bufLen : COCO_MIN_BUFFER_LENGTH; buf = new unsigned char[bufCapacity]; if (fileLen > 0) SetPos(0); // setup buffer to position 0 (start) else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid if (bufLen == fileLen && CanSeek()) Close(); } Buffer::Buffer(Buffer *b) { buf = b->buf; bufCapacity = b->bufCapacity; b->buf = NULL; bufStart = b->bufStart; bufLen = b->bufLen; fileLen = b->fileLen; bufPos = b->bufPos; stream = b->stream; b->stream = NULL; isUserStream = b->isUserStream; } Buffer::Buffer(const unsigned char* buf, int len) { this->buf = new unsigned char[len]; memcpy(this->buf, buf, len*sizeof(unsigned char)); bufStart = 0; bufCapacity = bufLen = len; fileLen = len; bufPos = 0; stream = NULL; } Buffer::~Buffer() { Close(); if (buf != NULL) { delete [] buf; buf = NULL; } } void Buffer::Close() { if (!isUserStream && stream != NULL) { fclose(stream); stream = NULL; } } int Buffer::Read() { if (bufPos < bufLen) { return buf[bufPos++]; } else if (GetPos() < fileLen) { SetPos(GetPos()); // shift buffer start to Pos return buf[bufPos++]; } else if ((stream != NULL) && !CanSeek() && (ReadNextStreamChunk() > 0)) { return buf[bufPos++]; } else { return EoF; } } int Buffer::Peek() { int curPos = GetPos(); int ch = Read(); SetPos(curPos); return ch; } // beg .. begin, zero-based, inclusive, in byte // end .. end, zero-based, exclusive, in byte wchar_t* Buffer::GetString(int beg, int end) { int len = 0; wchar_t *buf = new wchar_t[end - beg]; int oldPos = GetPos(); SetPos(beg); while (GetPos() < end) buf[len++] = (wchar_t) Read(); SetPos(oldPos); wchar_t *res = coco_string_create(buf, 0, len); coco_string_delete(buf); return res; } int Buffer::GetPos() { return bufPos + bufStart; } void Buffer::SetPos(int value) { if ((value >= fileLen) && (stream != NULL) && !CanSeek()) { // Wanted position is after buffer and the stream // is not seek-able e.g. network or console, // thus we have to read the stream manually till // the wanted position is in sight. while ((value >= fileLen) && (ReadNextStreamChunk() > 0)); } if ((value < 0) || (value > fileLen)) { wprintf(L"--- buffer out of bounds access, position: %d\n", value); exit(1); } if ((value >= bufStart) && (value < (bufStart + bufLen))) { // already in buffer bufPos = value - bufStart; } else if (stream != NULL) { // must be swapped in fseek(stream, value, SEEK_SET); bufLen = fread(buf, sizeof(unsigned char), bufCapacity, stream); bufStart = value; bufPos = 0; } else { bufPos = fileLen - bufStart; // make Pos return fileLen } } // Read the next chunk of bytes from the stream, increases the buffer // if needed and updates the fields fileLen and bufLen. // Returns the number of bytes read. int Buffer::ReadNextStreamChunk() { int free = bufCapacity - bufLen; if (free == 0) { // in the case of a growing input stream // we can neither seek in the stream, nor can we // foresee the maximum length, thus we must adapt // the buffer size on demand. bufCapacity = bufLen * 2; unsigned char *newBuf = new unsigned char[bufCapacity]; memcpy(newBuf, buf, bufLen*sizeof(unsigned char)); delete [] buf; buf = newBuf; free = bufLen; } int read = fread(buf + bufLen, sizeof(unsigned char), free, stream); if (read > 0) { fileLen = bufLen = (bufLen + read); return read; } // end of stream reached return 0; } bool Buffer::CanSeek() { return (stream != NULL) && (ftell(stream) != -1); } int UTF8Buffer::Read() { int ch; do { ch = Buffer::Read(); // until we find a utf8 start (0xxxxxxx or 11xxxxxx) } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EoF)); if (ch < 128 || ch == EoF) { // nothing to do, first 127 chars are the same in ascii and utf8 // 0xxxxxxx or end of file character } else if ((ch & 0xF0) == 0xF0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx int c1 = ch & 0x07; ch = Buffer::Read(); int c2 = ch & 0x3F; ch = Buffer::Read(); int c3 = ch & 0x3F; ch = Buffer::Read(); int c4 = ch & 0x3F; ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4; } else if ((ch & 0xE0) == 0xE0) { // 1110xxxx 10xxxxxx 10xxxxxx int c1 = ch & 0x0F; ch = Buffer::Read(); int c2 = ch & 0x3F; ch = Buffer::Read(); int c3 = ch & 0x3F; ch = (((c1 << 6) | c2) << 6) | c3; } else if ((ch & 0xC0) == 0xC0) { // 110xxxxx 10xxxxxx int c1 = ch & 0x1F; ch = Buffer::Read(); int c2 = ch & 0x3F; ch = (c1 << 6) | c2; } return ch; } Scanner::Scanner(const unsigned char* buf, int len) { buffer = new Buffer(buf, len); Init(); } Scanner::Scanner(const wchar_t* fileName) { FILE* stream; char *chFileName = coco_string_create_char(fileName); if ((stream = fopen(chFileName, "rb")) == NULL) { wprintf(L"--- Cannot open file %ls\n", fileName); exit(1); } coco_string_delete(chFileName); buffer = new Buffer(stream, false); Init(); } Scanner::Scanner(FILE* s) { buffer = new Buffer(s, true); Init(); } Scanner::~Scanner() { char* cur = (char*) firstHeap; while(cur != NULL) { cur = *(char**) (cur + COCO_HEAP_BLOCK_SIZE); free(firstHeap); firstHeap = cur; } delete [] tval; delete buffer; } void Scanner::Init() { EOL = '\n'; eofSym = 0; - maxT = 34; - noSym = 34; + maxT = 33; + noSym = 33; int i; for (i = 65; i <= 90; ++i) start.set(i, 1); for (i = 97; i <= 122; ++i) start.set(i, 1); for (i = 48; i <= 57; ++i) start.set(i, 2); start.set(34, 3); start.set(40, 5); start.set(41, 6); start.set(61, 7); start.set(58, 8); start.set(44, 9); - start.set(45, 21); - start.set(123, 11); - start.set(59, 12); - start.set(125, 13); - start.set(91, 14); - start.set(93, 15); - start.set(62, 16); + start.set(45, 10); + start.set(62, 11); + start.set(123, 12); + start.set(59, 13); + start.set(125, 14); + start.set(91, 15); + start.set(93, 16); start.set(43, 17); start.set(42, 18); start.set(47, 19); start.set(60, 20); start.set(Buffer::EoF, -1); keywords.set(L"function", 7); - keywords.set(L"string", 14); - keywords.set(L"int", 15); - keywords.set(L"num", 16); - keywords.set(L"float", 17); - keywords.set(L"bool", 18); + keywords.set(L"string", 15); + keywords.set(L"int", 16); + keywords.set(L"num", 17); + keywords.set(L"float", 18); + keywords.set(L"bool", 19); keywords.set(L"assert", 22); keywords.set(L"require", 23); keywords.set(L"rule", 24); keywords.set(L"case", 25); keywords.set(L"variable", 26); keywords.set(L"warning", 27); keywords.set(L"message", 28); tvalLength = 128; tval = new wchar_t[tvalLength]; // text of current token // COCO_HEAP_BLOCK_SIZE byte heap + pointer to next heap block heap = malloc(COCO_HEAP_BLOCK_SIZE + sizeof(void*)); firstHeap = heap; heapEnd = (void**) (((char*) heap) + COCO_HEAP_BLOCK_SIZE); *heapEnd = 0; heapTop = heap; if (sizeof(Token) > COCO_HEAP_BLOCK_SIZE) { wprintf(L"--- Too small COCO_HEAP_BLOCK_SIZE\n"); exit(1); } pos = -1; line = 1; col = 0; charPos = -1; oldEols = 0; NextCh(); if (ch == 0xEF) { // check optional byte order mark for UTF-8 NextCh(); int ch1 = ch; NextCh(); int ch2 = ch; if (ch1 != 0xBB || ch2 != 0xBF) { wprintf(L"Illegal byte order mark at start of file"); exit(1); } Buffer *oldBuf = buffer; buffer = new UTF8Buffer(buffer); col = 0; charPos = -1; delete oldBuf; oldBuf = NULL; NextCh(); } pt = tokens = CreateToken(); // first token is a dummy } void Scanner::NextCh() { if (oldEols > 0) { ch = EOL; oldEols--; } else { pos = buffer->GetPos(); // buffer reads unicode chars, if UTF8 has been detected ch = buffer->Read(); col++; charPos++; // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == L'\r' && buffer->Peek() != L'\n') ch = EOL; if (ch == EOL) { line++; col = 0; } } } void Scanner::AddCh() { if (tlen >= tvalLength) { tvalLength *= 2; wchar_t *newBuf = new wchar_t[tvalLength]; memcpy(newBuf, tval, tlen*sizeof(wchar_t)); delete [] tval; tval = newBuf; } if (ch != Buffer::EoF) { tval[tlen++] = ch; NextCh(); } } bool Scanner::Comment0() { int level = 1, pos0 = pos, line0 = line, col0 = col, charPos0 = charPos; NextCh(); if (ch == L'/') { NextCh(); for(;;) { if (ch == 10) { level--; if (level == 0) { oldEols = line - line0; NextCh(); return true; } NextCh(); } else if (ch == buffer->EoF) return false; else NextCh(); } } else { buffer->SetPos(pos0); NextCh(); line = line0; col = col0; charPos = charPos0; } return false; } bool Scanner::Comment1() { int level = 1, pos0 = pos, line0 = line, col0 = col, charPos0 = charPos; NextCh(); if (ch == L'*') { NextCh(); for(;;) { if (ch == L'*') { NextCh(); if (ch == L'/') { level--; if (level == 0) { oldEols = line - line0; NextCh(); return true; } NextCh(); } } else if (ch == L'/') { NextCh(); if (ch == L'*') { level++; NextCh(); } } else if (ch == buffer->EoF) return false; else NextCh(); } } else { buffer->SetPos(pos0); NextCh(); line = line0; col = col0; charPos = charPos0; } return false; } void Scanner::CreateHeapBlock() { void* newHeap; char* cur = (char*) firstHeap; while(((char*) tokens < cur) || ((char*) tokens > (cur + COCO_HEAP_BLOCK_SIZE))) { cur = *((char**) (cur + COCO_HEAP_BLOCK_SIZE)); free(firstHeap); firstHeap = cur; } // COCO_HEAP_BLOCK_SIZE byte heap + pointer to next heap block newHeap = malloc(COCO_HEAP_BLOCK_SIZE + sizeof(void*)); *heapEnd = newHeap; heapEnd = (void**) (((char*) newHeap) + COCO_HEAP_BLOCK_SIZE); *heapEnd = 0; heap = newHeap; heapTop = heap; } Token* Scanner::CreateToken() { Token *t; if (((char*) heapTop + (int) sizeof(Token)) >= (char*) heapEnd) { CreateHeapBlock(); } t = (Token*) heapTop; heapTop = (void*) ((char*) heapTop + sizeof(Token)); t->val = NULL; t->next = NULL; return t; } void Scanner::AppendVal(Token *t) { int reqMem = (tlen + 1) * sizeof(wchar_t); if (((char*) heapTop + reqMem) >= (char*) heapEnd) { if (reqMem > COCO_HEAP_BLOCK_SIZE) { wprintf(L"--- Too long token value\n"); exit(1); } CreateHeapBlock(); } t->val = (wchar_t*) heapTop; heapTop = (void*) ((char*) heapTop + reqMem); wcsncpy(t->val, tval, tlen); t->val[tlen] = L'\0'; } Token* Scanner::NextToken() { while (ch == ' ' || (ch >= 9 && ch <= 10) || ch == 13 ) NextCh(); if ((ch == L'/' && Comment0()) || (ch == L'/' && Comment1())) return NextToken(); int recKind = noSym; int recEnd = pos; t = CreateToken(); t->pos = pos; t->col = col; t->line = line; t->charPos = charPos; int state = start.state(ch); tlen = 0; AddCh(); switch (state) { case -1: { t->kind = eofSym; break; } // NextCh already done case 0: { case_0: if (recKind != noSym) { tlen = recEnd - t->pos; SetScannerBehindT(); } t->kind = recKind; break; } // NextCh already done case 1: case_1: recEnd = pos; recKind = 1; if ((ch >= L'0' && ch <= L'9') || (ch >= L'A' && ch <= L'Z') || (ch >= L'a' && ch <= L'z')) {AddCh(); goto case_1;} else {t->kind = 1; wchar_t *literal = coco_string_create(tval, 0, tlen); t->kind = keywords.get(literal, t->kind); coco_string_delete(literal); break;} case 2: case_2: recEnd = pos; recKind = 2; if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_2;} else {t->kind = 2; break;} case 3: case_3: - if ((ch >= L'A' && ch <= L'Z') || (ch >= L'a' && ch <= L'z')) {AddCh(); goto case_3;} + if (ch <= L'!' || (ch >= L'#' && ch <= 65535)) {AddCh(); goto case_3;} else if (ch == L'"') {AddCh(); goto case_4;} else {goto case_0;} case 4: case_4: {t->kind = 3; break;} case 5: {t->kind = 4; break;} case 6: {t->kind = 5; break;} case 7: {t->kind = 6; break;} case 8: {t->kind = 8; break;} case 9: {t->kind = 9; break;} case 10: - case_10: {t->kind = 10; break;} case 11: {t->kind = 11; break;} case 12: {t->kind = 12; break;} case 13: {t->kind = 13; break;} case 14: - {t->kind = 19; break;} + {t->kind = 14; break;} case 15: {t->kind = 20; break;} case 16: - {t->kind = 29; break;} + {t->kind = 21; break;} case 17: - {t->kind = 30; break;} + {t->kind = 29; break;} case 18: - {t->kind = 31; break;} + {t->kind = 30; break;} case 19: - {t->kind = 32; break;} + {t->kind = 31; break;} case 20: - {t->kind = 33; break;} - case 21: - recEnd = pos; recKind = 21; - if (ch == L'>') {AddCh(); goto case_10;} - else {t->kind = 21; break;} + {t->kind = 32; break;} } AppendVal(t); return t; } void Scanner::SetScannerBehindT() { buffer->SetPos(t->pos); NextCh(); line = t->line; col = t->col; charPos = t->charPos; for (int i = 0; i < tlen; i++) NextCh(); } // get the next token (possibly a token already seen during peeking) Token* Scanner::Scan() { if (tokens->next == NULL) { return pt = tokens = NextToken(); } else { pt = tokens = tokens->next; return tokens; } } // peek for the next token, ignore pragmas Token* Scanner::Peek() { do { if (pt->next == NULL) { pt->next = NextToken(); } pt = pt->next; } while (pt->kind > maxT); // skip pragmas return pt; } // make sure that peeking starts at the current scan position void Scanner::ResetPeek() { pt = tokens; } diff --git a/coco/xreate.ATG b/coco/xreate.ATG index ed7e702..7d4eb54 100644 --- a/coco/xreate.ATG +++ b/coco/xreate.ATG @@ -1,198 +1,194 @@ #include "ast.h" #include COMPILER Xreate AST root; // current program unit (procedure or main program) bool checkParametersList() { - scanner.ResetPeek(); - Token x = scanner->Peek(); + scanner->ResetPeek(); + Token* x = scanner->Peek(); return la->kind == _ident && x->kind == _lparen; } bool checkInfix() { - scanner.ResetPeek(); - Token x = scanner->Peek(); + scanner->ResetPeek(); + Token* x = scanner->Peek(); return la->kind == _ident && x->kind == _ident; } -bool check - CHARACTERS letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". + any = ANY - '"'. digit = "0123456789". cr = '\r'. lf = '\n'. tab = '\t'. TOKENS ident = letter {letter | digit}. number = digit {digit}. - string = '"' {letter} '"'. + string = '"' { any } '"'. lparen = '('. rparen = ')'. COMMENTS FROM "/*" TO "*/" NESTED COMMENTS FROM "//" TO lf IGNORE cr + lf + tab PRODUCTIONS Xreate = { ( FDecl | RuleDecl ) }. Ident = ident (. name = t->val; .). FDecl<> = (. std::wstring fname; Expression fbody; std::wstring varname; TypeAnnotation typIn; TypeAnnotation typOut; .) Ident '=' "function" ':' (. Function f = Function(fname); .) ( Type (. f.setReturnType(typOut); .) | '('Ident ':' Type (. f.addArg(varname, typIn); .) {',' Ident ':' Type (. f.addArg(varname, typIn);.) -} ')' "->" Type (. f.setReturnType(typOut); .) +} ')' '-' '>' Type (. f.setReturnType(typOut); .) {',' FnTag } ) '{' {VDecl ';' } Expr ';' '}' (. f.setBody(fbody); root.add(f); .) . TypeTerm = ("string" | "int" | "num" | "float" | "bool") (. typ = Atom(t->val); .) . Type = (. TypeAnnotation typ2; TypeAtom typ3; .) ('[' Type ']' (. typ = TypeAnnotation(TypeOperator::LIST, typ2); .) | TypeTerm (. typ = TypeAnnotation(typ3); .) ) . VDecl = (. std::wstring vname; Expression e; TypeAnnotation typ; .) Ident '=' (Expr ':' Type (. f.addDeclaration(vname, typ, e); .) |ListLiteral ':' Type (. f.addListDeclaration(vname, typ, e); .) ). /*============================ METAPROGRAMMING ===============================*/ FnTag = (.std::wstring tag; TagModifier mod = TagModifier::NONE; .) Ident ['-' TagMod] (. f.addTag(tag, mod); .). TagMod = ( "assert" (. mod = TagModifier::ASSERT; .) | "require" (. mod = TagModifier::REQUIRE; .) ). RuleDecl<> = -"rule" ':' (. RuleArguments args; RuleGuards guards; RuleBody body; DomainAnnotation typ; .) +"rule" ':' (. RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; .) '(' Ident ':' Domain (. args.add(arg, typ); .) {',' Ident ':'Domain (. args.add(arg, typ); .) } ')' ["case" RGuard {',' RGuard}] (. .) '{' RBody '}' . Domain = ( "function" (. dom = DomainAnnotation::FUNCTION; .) | "variable" (. dom = DomainAnnotation::VARIABLE; .) ). -RGuard= (. MetaExpression e; .) -MetaExpr (. guards.add(e); .). +RGuard= (. Expression e; .) +MetaExpr (. guards.add(std::move(e)); .). -MetaExpr= +MetaExpr= (.Operator op; Expression e2; .) MetaExpr2 -[MetaOp MetaExpr2 (. MetaParameters params; - params.add(e); - params.add(e2); - e = MetaExpression(op, params); .) +[MetaOp MetaExpr2 (. e = Expression(op, {e, e2}); .) ]. -MetaExpr2= (. std::wstring i1, i2, infix; MetaParameters params; .) -( IF(checkParametersList()) Ident '(' [ MetaParams ] ')' - (. e = MetaExpression(Operator::CALL, i1, params); .) +MetaExpr2= (. std::wstring i1, i2, infix; .) +( IF(checkParametersList()) Ident (. e = Expression(Operator::CALL, {Expression(Atom(i1))}); .) +'(' [ CalleeParams ] ')' | IF(checkInfix()) Ident Ident Ident - (. params.add(Expression(Atom(i1->val))); - params.add(Expression(Atom(i2->val))); - e = MetaExpression(Operator::CALL, Atom(infix->val), params); .) -| Ident (. e = MetaExpression(Atom(i1->val)); .) + (. e = Expression(Operator::CALL, {Expression(Atom(infix))}); + e.addArg(Expression(Atom(i1))); + e.addArg(Expression(Atom(i2))); + .) +| Ident (. e = Expression(Atom(i1)); .) | '(' MetaExpr ')' ). -MetaParams = (. MetaExpr e; .) -MetaExpr (. params.add(e); .) -{',' MetaExpr (. params.add(e).) -}. - -RBody = (. MetaExpr e; .) - "warning" MetaExpr ["message" string] . +RBody = + (. Expression e; std::wstring msg; .) + "warning" MetaExpr ["message" string (. msg = t->val; .) +] (. root.add(new RuleWarning(RuleArguments(args), RuleGuards(guards), std::move(e), Atom(msg))); .) + . MetaOp< Operator& op> = - '-' '>' (. op = OPERATOR::IMPL; .) + '-' '>' (. op = Operator::IMPL; .) . /*============================ Expressions ===============================*/ +// dublicates CalleeParams ListLiteral = (. Expression e2; .) -'[' (. e = Expression(Operator::LIST, Expression()); .) -[ Expr (. e.addArg(e2); .) -{',' Expr (. e.addArg(e2); .) +'[' (. e.setOp(Operator::LIST); .) +[ Expr (. e.addArg(std::move(e2)); .) +{',' Expr (. e.addArg(std::move(e2)); .) }] ']'. Expr< Expression& e> (. Operator op; Expression e2; .) = SimExpr< e> [ RelOp< op> - SimExpr< e2> (. e = Expression(op, e); e.addArg(e2); .) +SimExpr< e2> (. e = Expression(op, {e, e2}); .) ]. SimExpr< Expression& e> (. Operator op; Expression e2; .) = Term< e> { AddOp< op> - Term< e2> (. e = Expression(op, e); e.addArg(e2);.) + Term< e2> (. e = Expression(op, {e, e2});.) }. Term< Expression& e> (. Operator op; Expression e2; .) = Factor< e> { MulOp< op> - Factor< e2> (. e = Expression(op, e); e.addArg(e2); .) + Factor< e2> (. e = Expression(op, {e, e2}); .) }. Factor< Expression& e> (. std::wstring name;.) = (IF (checkParametersList()) Ident< name> - (. e = Expression(Operator::CALL, Atom(name)); .) + (. e = Expression(Operator::CALL, {Atom(name)}); .) '(' [CalleeParams] ')' | Ident< name> (. e = Expression(Atom(name)); .) | number (. e = Expression(Atom(t->val)); .) - | '-' Factor< e> (. e = Expression(Operator::NEG, e); .) + | '-' Factor< e> (. e = Expression(Operator::NEG, {e}); .) | '(' Expr ')' ). CalleeParams = (. Expression e2; .) - Expr (. e.addArg(e2); .) - {',' Expr (. e.addArg(e2); .) + Expr (. e.addArg(std::move(e2)); .) + {',' Expr (. e.addArg(std::move(e2)); .) }. AddOp< Operator& op> = (. op = Operator::ADD; .) ( '+' | '-' (. op = Operator::SUB; .) ). MulOp< Operator& op> = (. op = Operator::MUL; .) ( '*' | '/' (. op = Operator::DIV; .) ). RelOp< Operator& op> = (. op = Operator::EQU; .) ( '=' '=' | '<' (. op = Operator::LSS; .) | '>' (. op = Operator::GTR; .) ). END Xreate. diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 58c7116..e4f435f 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,68 +1,69 @@ cmake_minimum_required(VERSION 2.8.11) project(Xreate) find_package(LLVM REQUIRED CONFIG) find_package(Qt5Core) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(SOURCE_FILES ./src/ast.cpp ./src/ast-compilation.cpp ./src/llvmlayer.cpp ./src/clasplayer.cpp #./src/main.cpp ./tests/tests.cpp ./src/pass/cfgpass.cpp ./src/pass/functiontagspass.cpp + ./src/pass/rulespass.cpp /opt/potassco/gringo-4.4.0-source/app/shared/src/clingocontrol.cc /opt/potassco/gringo-4.4.0-source/app/pyclingo/src/clingo_lib.cc ) set(COCO_PATH "./../coco/") set(COCO_SOURCE_FILES ${COCO_PATH}/Parser.h ${COCO_PATH}/Scanner.h ${COCO_PATH}/Parser.cpp ${COCO_PATH}/Scanner.cpp ) set(POTASSCO_PATH "/opt/potassco/gringo-4.4.0-source") INCLUDE_DIRECTORIES(${COCO_PATH} ./src) INCLUDE_DIRECTORIES(${POTASSCO_PATH}/libgringo ${POTASSCO_PATH}/libclasp ${POTASSCO_PATH}/app/shared/include ${POTASSCO_PATH}/app/pyclingo/src ${POTASSCO_PATH}/libprogram_opts ) execute_process(COMMAND ${COCO_PATH}/gen-xreate WORKING_DIRECTORY ${COCO_PATH} OUTPUT_VARIABLE COCO_OUTPUT) message(STATUS "COCO GRAMMAR BUILD STATUS:" ${COCO_OUTPUT}) include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) add_executable(xreate ${SOURCE_FILES} ${COCO_SOURCE_FILES}) llvm_map_components_to_libnames(llvm_libs support core irreader) message(STATUS "LLVM LIBS: " ${llvm_libs}) set (LIBCLASP_PATH ${POTASSCO_PATH}/build/debug) #find_library(LIBCLASP_LIBRARY_GRINGO NAMES gringo # HINTS ${LIBCLASP_PATH}) #find_library(LIBCLASP_LIBRARY_CLASP NAMES clasp # HINTS ${LIBCLASP_PATH}) set(LIBCLASP_LIBS ${LIBCLASP_PATH}/libclasp.a ${LIBCLASP_PATH}/libgringo.a ${LIBCLASP_PATH}/libprogram_opts.a ) message(STATUS ${LIBCLASP_LIBS}) target_link_libraries(xreate ${llvm_libs} pthread Qt5::Core ${LIBCLASP_LIBS}) add_definitions(-DWITH_THREADS=0) diff --git a/cpp/src/ast-compilation.cpp b/cpp/src/ast-compilation.cpp index 118252a..bfa81ab 100644 --- a/cpp/src/ast-compilation.cpp +++ b/cpp/src/ast-compilation.cpp @@ -1,168 +1,168 @@ #include llvm::Value* -LLVMExpression::compile(LLVMLayer& l, Function* f, std::string* hintRetVar) const +Function::compileExpression(const Expression& expr, LLVMLayer& l, std::string* hintRetVar=0) { std::string var; - if (hintRetVar && f->__vartable.count(*hintRetVar)) + if (hintRetVar && this->__vartable.count(*hintRetVar)) { var = *hintRetVar; } #define VARNAME(x) (var.size()? var : x) llvm::Value* left; llvm::Value* right; - switch (__op) + switch (expr.__op) { case Operator::ADD: case Operator::SUB: case Operator::MUL: case Operator::DIV: case Operator::EQU: case Operator::LSS: case Operator::GTR: - assert(__source.__state == COMPOUND); - assert(__source.operands.size() == 2); + assert(expr.__state == Expression::COMPOUND); + assert(expr.operands.size() == 2); - left = __source.operands[0].compile(l, f); - right = __source.operands[1].compile(l, f); + left = compileExpression(expr.operands[0], l); + right = compileExpression(expr.operands[1], l); break; default: ; } - switch (__op) + switch (expr.__op) { case Operator::ADD: return l.builder.CreateFAdd(left, right, VARNAME("tmp_add")); break; case Operator::SUB: return l.builder.CreateFSub(left, right, VARNAME("tmp_sub")); break; case Operator::MUL: return l.builder.CreateFMul(left, right, VARNAME("tmp_mul")); break; case Operator::DIV: return l.builder.CreateFDiv(left, right, VARNAME("tmp_div")); break; case Operator::EQU: return l.builder.CreateFCmpOEQ(left, right, VARNAME("tmp_equ")); break; case Operator::LSS: return l.builder.CreateFCmpOLT(left, right, VARNAME("tmp_lss")); break; case Operator::GTR: return l.builder.CreateFCmpOGT(left, right, VARNAME("tmp_gtr")); break; case Operator::NEG: - left = __source.operands[0].compile(l, f); + left = compileExpression(expr.operands[0], l); return l.builder.CreateFNeg(left, VARNAME("tmp_neg")); break; case Operator::CALL: { - assert(__source.__state == COMPOUND); + assert(expr.__state == Expression::COMPOUND); - const std::string& fname = __source.__valueS; - assert(f->root->__indexFunctions.count(fname)); + const std::string& fname = expr.__valueS; + assert(root->__indexFunctions.count(fname)); - const Function& calleeFunc = f->root->getFunctionById(f->root->__indexFunctions[fname]); + const Function& calleeFunc = root->getFunctionById(root->__indexFunctions[fname]); llvm::Function* callee = calleeFunc.__raw; std::vector args; - args.reserve(operands.size()-1); + args.reserve(expr.operands.size()-1); - std::transform(__source.operands.begin(), __source.operands.end(), std::inserter(args, args.end()), - [&l, f](const Expression &operand){return compile(operand, f); } + std::transform(expr.operands.begin(), expr.operands.end(), std::inserter(args, args.end()), + [&l, this](const Expression &operand){return compileExpression(operand, l); } ); return l.builder.CreateCall(callee, args, VARNAME("tmp_call")); } break; case Operator::NONE: - assert(__source.__state != COMPOUND); + assert(expr.__state != Expression::COMPOUND); - switch (__source.__state) + switch (expr.__state) { - case IDENT: + case Expression::IDENT: { - std::string vname = __source.__valueS; - assert(f->__vartable.count(vname)); + std::string vname = expr.__valueS; + assert(__vartable.count(vname)); - VID vId =f->__vartable[vname]; - if (f->__rawVars.count(vId)) + VID vId =__vartable.at(vname); + if (__rawVars.count(vId)) { - return f->__rawVars[vId]; + return this->__rawVars.at(vId); } - Expression& e = f->__declarations[vId]; - llvm::Value* result = e.compile(l, f, &vname); - f->__rawVars[vId] = result; + Expression& e = __declarations[vId]; + llvm::Value* result = compileExpression(e, l, &vname); + __rawVars[vId] = result; return result; break; } - case NUMBER: - double literal = __valueD; + case Expression::NUMBER: + double literal = expr.__valueD; return llvm::ConstantFP::get(llvm::getGlobalContext(), llvm::APFloat(literal)); }; break; } assert(false); return 0; } llvm::Function* Function::compile(LLVMLayer &l) { std::vector types; std::transform(__args.begin(), __args.end(), std::inserter(types, types.end()), [this](std::string& arg) { assert(__vartable.count(arg)); VID argid = __vartable[arg]; assert(__definitions.count(argid)); return __definitions[argid].toLLVMType(); } ); llvm::FunctionType* ft = llvm::FunctionType::get(__retType.toLLVMType(), types,false); __raw = llvm::cast(l.module->getOrInsertFunction(__name,ft)); llvm::Function::arg_iterator fargsI = __raw->arg_begin(); for(std::string& arg : __args) { VID argid = __vartable[arg]; __rawVars[argid] = fargsI; fargsI->setName(arg); ++fargsI; } llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm::getGlobalContext(), "entry", __raw); l.builder.SetInsertPoint(block); - l.builder.CreateRet(__body.compile(l, this, 0)); + l.builder.CreateRet(compileExpression(__body, l, 0)); l.moveToGarbage(ft); return __raw; }; void AST::compile(LLVMLayer &layer) { layer.module = new llvm::Module(getModuleName(),llvm::getGlobalContext()); for(Function& f: __functions) { llvm::Function* rawf = f.compile(layer); } } diff --git a/cpp/src/ast.cpp b/cpp/src/ast.cpp index fcc157f..3faabe7 100644 --- a/cpp/src/ast.cpp +++ b/cpp/src/ast.cpp @@ -1,236 +1,243 @@ #include "ast.h" #include #include #include #include using namespace std; TypeAnnotation::TypeAnnotation() {} TypeAnnotation::TypeAnnotation(const Atom &typ) { __value = typ.get(); } TypeAnnotation::TypeAnnotation(const TypeOperator &op, const TypeAnnotation &typ) {} llvm::Type* TypeAnnotation::toLLVMType() { switch (__value) { case Bool: return llvm::Type::getInt1Ty(llvm::getGlobalContext()); case Int: case Float: case Num: return llvm::Type::getDoubleTy(llvm::getGlobalContext()); default: assert(false); } return NULL; } Expression::Expression(const Atom& number) : __state(NUMBER), __op(Operator::NONE), __valueD(number.get()) { } Expression::Expression(const Atom &ident) : __state(IDENT), __op(Operator::NONE), __valueS(ident.get()) { } Expression::Expression(const Operator &op, std::initializer_list params) : __state(COMPOUND), __op(op) { if (op == Operator::CALL) { assert(params.size() > 0); Expression arg = *params.begin(); assert(arg.__state == Expression::IDENT); __valueS = std::move(arg.__valueS); return; } operands.insert(operands.end(), params); } void Expression::setOp(Operator op) { __op = op; switch (op) { case Operator::NONE: __state = INVALID; break; default: __state = COMPOUND; break; } } void Expression::addArg(Expression &&arg) { operands.push_back(arg); } Expression::Expression() : __op(Operator::NONE), __state(INVALID) {} AST::AST() { } void AST::add(Function &f) { f.root = this; __functions.push_back(f); __indexFunctions[f.getName()] = __functions.size()-1; } +void +AST::add(MetaRuleAbstract *r) +{ + __rules.push_back(unique_ptr(r)); +} + std::string AST::getModuleName() { const std::string name = "moduleTest"; return name; } FID AST::getFunctionsCount() const { return __functions.size(); } const Function& AST::getFunctionById(FID id)const { assert(id>=0 && id < __functions.size()); return __functions[id]; } void AST::run(LLVMLayer &l) { llvm::PassManager PM; PM.add(llvm::createPrintModulePass(llvm::outs())); PM.run(*l.module); } -Function::Function(const wstring &name) +Function::Function(const Atom& name) :__vCounter(0) { - char buffer[1000]; - wcstombs(buffer, name.c_str(), 1000); - __name = buffer; + __name = name.get(); + } void -Function::addAnnotation(const wstring& aname, const AnnotationModifier amod) +Function::addTag(const Atom& aname, const TagModifier amod) { - char buffer[1000]; - wcstombs(buffer, aname.c_str(), 1000); - std::string name(buffer); - - cout << QString("Function annotation: %1 <- %2").arg(__name.c_str()).arg(name.c_str()).toStdString()<>& +const std::vector>& Function::getAnnotations() const { return __tags; } void -Function::addArg(const wstring &vname, const TypeAnnotation &typ) +Function::addArg(const Atom &vname, const TypeAnnotation &typ) { - char buffer[1000]; - wcstombs(buffer, vname.c_str(), 1000); - std::string name(buffer); - - VID id = registerVar(name); + VID id = registerVar(vname.get()); __definitions[id] = typ; - __args.push_back(name); + __args.push_back(vname.get()); } void Function::setBody(const Expression &body) { __body = body; } void Function::setReturnType(const TypeAnnotation &rtyp) { __retType = rtyp; } VID Function::registerVar(const std::string& vname) { __vartable[vname] = ++__vCounter; return __vCounter; } const std::string& Function::getName() const { return __name; } void -Function::addListDeclaration(const wstring &vname, const TypeAnnotation &typ, const Expression &e) +Function::addListDeclaration(const Atom &vname, const TypeAnnotation &typ, const Expression &e) {} void -Function::addDeclaration(const wstring &vname, const TypeAnnotation &typ, const Expression &e) +Function::addDeclaration(const Atom &vname, const TypeAnnotation &typ, const Expression &e) { - char buffer[1000]; - wcstombs(buffer, vname.c_str(), 1000); - - std::string v(buffer); - - VID id = registerVar(v); + VID id = registerVar(vname.get()); __definitions[id] = typ; __declarations[id] = e; } void -RuleArguments::add(std::string&& name, DomainAnnotation typ) +RuleArguments::add(const Atom &arg, DomainAnnotation typ) { - emplace_back(name, typ); + emplace_back(arg.get(), typ); } void RuleGuards::add(Expression&& e) { push_back(e); } +MetaRuleAbstract:: +MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards) + : __args(std::move(args)), __guards(std::move(guards)) +{} + +MetaRuleAbstract::~MetaRuleAbstract(){} + +RuleWarning:: +RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message) + : MetaRuleAbstract(std::move(args), std::move(guards)), __condition(condition), __message(message.get()) +{} + +RuleWarning::~RuleWarning(){} + void RuleWarning::compile(ClaspLayer& layer) { - layer.addRuleWarning(this); + layer.addRuleWarning(*this); } + diff --git a/cpp/src/ast.h b/cpp/src/ast.h index 22b8597..52645f0 100644 --- a/cpp/src/ast.h +++ b/cpp/src/ast.h @@ -1,248 +1,276 @@ #ifndef AST_H #define AST_H #include #include #include #include "llvmlayer.h" +#include +struct String_t{}; struct Identifier_t {}; struct Number_t {}; struct Type_t {}; template -class Atom{ - -}; +class Atom{}; template<> class Atom { public: Atom(const std::wstring& value) { - char buffer[1000]; - wcstombs(buffer, value.c_str(), 1000); + char buffer[32]; + wcstombs(buffer, value.c_str(), 32); __value = buffer; } const std::string& get() const{return __value; } private: std::string __value; }; template<> class Atom { public: Atom(wchar_t* value) { __value = wcstol(value, 0, 10); } - int get()const {return __value; } + double get()const {return __value; } private: double __value; }; +template<> class Atom +{ +public: + Atom(const std::wstring& value) + : __value(value.begin(), value.end()) + { + } + const std::string& get() const {return __value; } + +private: + std::string __value; +}; + enum TimePrimitive {Bool, Int, Float, Num, String}; template<> class Atom { public: Atom(wchar_t* value) { - char buffer_[1000]; - wcstombs(buffer_, value, 1000); + char buffer_[32]; + wcstombs(buffer_, value, 32); std::string buffer(buffer_); if (buffer=="bool"){ __value = Bool; } else if (buffer=="int") { __value = Int; } else if (buffer=="float") { __value = Float; } else if (buffer=="num") { __value = Num; } else if (buffer=="string") { __value = String; } } Atom () { } TimePrimitive get() const { return __value; } private: TimePrimitive __value; }; typedef Atom TypeAtom; enum class TypeOperator{LIST}; class TypeAnnotation { public: TypeAnnotation (const Atom& typ); TypeAnnotation (const TypeOperator& op, const TypeAnnotation& typ); TypeAnnotation(); llvm::Type* toLLVMType(); private: TimePrimitive __value; }; enum class Operator { ADD, SUB, MUL, DIV, EQU, LSS, GTR, NEG, LIST, CALL, NONE, IMPL/* implication */ }; class Function; class AST; class Expression { + friend class Function; + friend class ClaspLayer; + friend class CFGPass; public: Expression(const Operator &op, std::initializer_list params); Expression(const Atom& ident); Expression(const Atom& number); Expression(); void setOp(Operator op); void addArg(Expression&& arg); protected: Operator __op; std::vector operands; std::string __valueS; double __valueD; enum {INVALID, COMPOUND, IDENT, NUMBER, STRING} __state; }; -enum class AnnotationModifier +typedef std::list ExpressionList; +enum class TagModifier {NONE, ASSERT, REQUIRE}; enum class DomainAnnotation {FUNCTION, VARIABLE}; class RuleArguments: public std::vector> { public: - void add(std::string&& name, DomainAnnotation typ); + void add(const Atom& name, DomainAnnotation typ); }; class RuleGuards: public std::vector { public: void add(Expression&& e); }; + +class ClaspLayer; + class MetaRuleAbstract { +public: + MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards); + virtual ~MetaRuleAbstract(); + virtual void compile(ClaspLayer& layer) =0; protected: RuleArguments __args; RuleGuards __guards; }; -class ClaspLayer; class RuleWarning: public MetaRuleAbstract { friend class ClaspLayer; public: - void compile(ClaspLayer& layer); + RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message); + virtual void compile(ClaspLayer& layer); + ~RuleWarning(); private: std::string __message; Expression __condition; }; typedef unsigned int VID; /* class Expression: ExpressionAbstract { friend class CFGPass; public: llvm::Value* compile(LLVMLayer& l, Function* f, std::string* hintRetVar=0) const; }; */ typedef std::pair VariableDefinition; typedef std::pair VariableDeclaration; class Function { friend class Expression; friend class CFGPass; public: - Function(const std::wstring& name); + Function(const Atom& name); void setReturnType(const TypeAnnotation& rtyp); - void addArg(const std::wstring& vname, const TypeAnnotation& typ); + void addArg(const Atom& vname, const TypeAnnotation& typ); void setBody(const Expression& body); - void addAnnotation(const std::wstring& aname, const AnnotationModifier amod); - const std::vector>& getAnnotations() const; + void addTag(const Atom& aname, const TagModifier amod); + const std::vector>& getAnnotations() const; - void addDeclaration(const std::wstring& vname, const TypeAnnotation& typ, const Expression& e); - void addListDeclaration(const std::wstring& vname, const TypeAnnotation& typ, const Expression& e); + void addDeclaration(const Atom & vname, const TypeAnnotation& typ, const Expression& e); + void addListDeclaration(const Atom & vname, const TypeAnnotation& typ, const Expression& e); const std::string& getName() const; llvm::Function* compile(LLVMLayer& l); AST* root; private: std::string __name; TypeAnnotation __retType; std::vector __args; - std::vector> __tags; + std::vector> __tags; std::map __definitions; std::map __declarations; std::map __vartable; Expression __body; llvm::Function* __raw; std::map __rawVars; VID __vCounter; VID registerVar(const std::string& vname); - llvm::Value* compileExpression(Expression& expr, LLVMLayer& l, std::string* hintRetVar) const; + llvm::Value* compileExpression(const Expression& expr, LLVMLayer& l, std::string* hintRetVar); }; typedef unsigned int FID; class AST { - friend class Expression; + friend class Function; friend class CFGPass; + friend class RulesPass; public: AST(); void add(Function& f); + void add(MetaRuleAbstract* r); void compile(LLVMLayer& l); void run(LLVMLayer& l); std::string getModuleName(); FID getFunctionsCount() const; const Function& getFunctionById(FID id) const; private: + std::list> __rules; std::vector __functions; std::map __indexFunctions; }; #endif // AST_H diff --git a/cpp/src/clasplayer.cpp b/cpp/src/clasplayer.cpp index a657703..fe75d6d 100644 --- a/cpp/src/clasplayer.cpp +++ b/cpp/src/clasplayer.cpp @@ -1,276 +1,306 @@ #include "clasplayer.h" #include // for defining logic programs #include // unfounded set checkers #include // for enumerating answer sets #include #include #include #include -#include -#include + using namespace std; bool ClaspLayer::onModel(Gringo::Model const & model) { + std::list warnings; cout << "Model: " << endl; for (Gringo::Value atom : model.atoms(Gringo::Model::ATOMS)) { atom.print(cout); cout << endl; + + auto hookI = __hooks.find(*(atom.name())); + + if (hookI != __hooks.end()) { + warnings.push_back(hookI->second); + } + } + + for (auto warning: warnings) + { + cout << "Warning: "<< warning << endl; } } QStringList multiplyLists(std::list&& lists) { - QStringList&& result = lists.front(); + QStringList result = lists.front(); lists.pop_front(); for (QStringList& list: lists) { QStringList::const_iterator end = result.end(); for (QStringList::iterator expr1I=result.begin(); expr1I& relation: graph.__relations) { const string& tagFrom = graph.__nodes.at(relation.first); const string& tagTo = graph.__nodes.at(relation.second); cout << (QString("call(%1, %2) .").arg(tagFrom.c_str()).arg(tagTo.c_str())).toStdString()<& tags) { ostream& cout = __partTags; cout << (QString("function(%1) .").arg(function.c_str())).toStdString()<__args.begin(), rule->__args.end(), std::inserter(domains, domains.begin()), - [](const auto& argument) { + QStringList domains; + std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(domains, domains.begin()), + [](const std::pair& argument) { char* domain; switch (argument.second) { case DomainAnnotation::FUNCTION: domain = "function"; break; case DomainAnnotation::VARIABLE: domain = "variable"; break; } return QString("%1(%2)").arg(domain).arg(argument.first.c_str()); }); - std::QStringList vars; - std::transform(rule->__args.begin(), rule->__args.end(), std::inserter(vars, vars.begin()), - [](const auto& argument) { + QStringList vars; + std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(vars, vars.begin()), + [](const std::pair& argument) { return argument.first.c_str(); }); - std::QStringList guards; - std::transform(rule->__guards.begin(), rule->__guards.end(), std::inserter(guards, guards.begin()), - [](const MetaExpression& guard) { - return compile(guard)._c_str(); + std::list guardsRaw; + std::transform(rule.__guards.begin(), rule.__guards.end(), std::inserter(guardsRaw, guardsRaw.begin()), + [this](const Expression& guard) { + return compile(guard); }); - QStringList&& branches = compileNeg(rule->__condition); + QStringList guards = multiplyLists(std::move(guardsRaw)); + QStringList&& branches = compileNeg(rule.__condition); - for(const std::string& branch: branches) + for (const QString& guardsJoined: guards) + for(const QString& branch: branches) { - std::string listener = registerListener(rule->__message); + const std::string&& hook = registerHook(rule.__message); - result = QString("%1(%2):-%3, %4, %5") - .arg(listener.c_str()) + QString result = QString("%1(%2):-%3, %4, %5.") + .arg(hook.c_str()) .arg(vars.join(", ")) - .arg(branch.c_str()) - .arg(guards.join(", ")) + .arg(branch) + .arg(guardsJoined) .arg(domains.join(", ")); - __partGeneral<< result << endl; + __partGeneral<< result.toStdString() << endl; } } QStringList ClaspLayer::compile(const Expression& e) const { QStringList result; switch(e.__op) { case Operator::CALL: - assert(e.__state == COMPOUND); + { + assert(e.__state == Expression::COMPOUND); std::list operands; - std::transform(e.operands.begin(), e.operands.end(), std::inserter(operands, operands().begin()), - [](const Expression& e) {return compile(e);}); + std::transform(e.operands.begin(), e.operands.end(), std::inserter(operands, operands.begin()), + [this](const Expression& e) {return compile(e);}); - QStringList&& operands_ = multiplyLists(operands); + QStringList&& operands_ = multiplyLists(std::move(operands)); result.append(QString("%1(%2)").arg(e.__valueS.c_str()).arg(operands_.join(", "))); break; + } case Operator::NONE: + { switch (e.__state) { case Expression::IDENT: - result.append(e.__valueS); + result.append(QString(e.__valueS.c_str())); break; case Expression::NUMBER: - result.append(e.__valueD); + result.append(QString::number(e.__valueD)); break; default: assert(true); } break; } + } return result; } -QStringList compileNeg(const Expression& e) const +QStringList +ClaspLayer::compileNeg(const Expression& e) const { QStringList result; - switch(__op) + switch(e.__op) { case Operator::IMPL: - assert(e.__state == COMPOUND); + { + assert(e.__state == Expression::COMPOUND); assert(e.operands.size() == 2); QStringList operands1 = compile(e.operands.at(0)); QStringList operands2 = compile(e.operands.at(1)); for (const auto& op1: operands1) for (const auto& op2: operands2) { result.append(QString("%1, not %2").arg(op1).arg(op2)); } break; + } default: assert(true); } return result; } +std::string +ClaspLayer::registerHook(const std::string& message) +{ + static int i=0; + std::string hookId = "hook" + std::to_string(++i); + __hooks.insert(make_pair(hookId, message)); + + return hookId; +} + void ClaspLayer::run() { ostringstream program; program << __partTags.str() << __partGeneral.str(); cout << program.str() << endl; const char* argv[] = {nullptr, nullptr}; ClingoLib ctl(2, argv); //prg.add("p", ["t"], "q(t).") Gringo::FWStringVec vars{}; ctl.add("base", vars, program.str()); //prg.ground([("p", [2])]) Gringo::Control::GroundVec vals{std::make_pair("base", Gringo::FWValVec {})}; ctl.ground(vals, Gringo::Any()); //solve Gringo::Control::Assumptions as; Gringo::SolveResult result = ctl.solve(Gringo::Control::ModelHandler([&](Gringo::Model const &model){return this->onModel(model);}), std::move(as)); if (result == Gringo::SolveResult::SAT) { cout << "SUCCESSFULLY" << endl; } else { cout << "UNSUCCESSFULLY" << endl; } } ClaspLayer::ClaspLayer() {} /* void AspOutPrinter::reportSolution(const Clasp::Solver&, const Clasp::Enumerator&, bool complete) { if (complete) std::cout << "No more models!" << std::endl; else std::cout << "More models possible!" << std::endl; } void AspOutPrinter::reportModel(const Clasp::Solver& s, const Clasp::Enumerator&) { std::cout << "Model " << s.stats.solve.models << ": \n"; // get the symbol table from the solver const Clasp::AtomIndex& symTab = *s.strategies().symTab; for (Clasp::AtomIndex::const_iterator it = symTab.begin(); it != symTab.end(); ++it) { // print each named atom that is true w.r.t the current assignment } std::cout << std::endl; } */ /***************************************** * CFGraph ***************************************** */ void CFGraph::addNode(FID function, std::string&& tag) { __nodes.insert(make_pair(function, move(tag))); } bool CFGraph::existsNode(FID function) const { return __nodes.count(function); } void CFGraph::addLink(FID nodeFrom, FID nodeTo) { assert(__nodes.count(nodeFrom)); assert(__nodes.count(nodeTo)); __relations.insert(std::make_pair(nodeFrom, nodeTo)); } diff --git a/cpp/src/clasplayer.h b/cpp/src/clasplayer.h index 7a8495e..c4ba9a5 100644 --- a/cpp/src/clasplayer.h +++ b/cpp/src/clasplayer.h @@ -1,44 +1,50 @@ #ifndef CLASPLAYER_H #define CLASPLAYER_H #include #include #include +#include -typedef std::pair Tag; +typedef std::pair Tag; class CFGraph; class ClaspLayer { public: ClaspLayer(); void addFunctionTags(const std::string& function, const std::vector& tags); void addCFGData(CFGraph&& graph); - void addWarningRule(const char* rule); + void addRuleWarning(const RuleWarning& rule); void run(); private: + std::map __hooks; std::ostringstream __partTags; std::ostringstream __partGeneral; bool onModel(Gringo::Model const & model); + + QStringList compile(const Expression& e) const; + QStringList compileNeg(const Expression& e) const; + std::string registerHook(const std::string& message); }; class CFGraph { friend class ClaspLayer; public: void addNode(FID function, std::string&& tag); void addLink(FID nodeFrom, FID nodeTo); bool existsNode(FID function) const; void print(std::ostream& cout) const; private: std::map __relations; std::map __nodes; }; #endif diff --git a/cpp/src/llvmlayer.cpp b/cpp/src/llvmlayer.cpp index 99a16b7..97f5978 100644 --- a/cpp/src/llvmlayer.cpp +++ b/cpp/src/llvmlayer.cpp @@ -1,17 +1,13 @@ #include "llvmlayer.h" using namespace llvm; LLVMLayer::LLVMLayer() :builder(getGlobalContext()) { } void LLVMLayer::moveToGarbage(void *o) { __garbage.push_back(o); } - -LLVMExpression::LLVMExpression(Expression &&e) - : Delegate(e) -{} diff --git a/cpp/src/pass/rulespass.cpp b/cpp/src/pass/rulespass.cpp new file mode 100644 index 0000000..c97577c --- /dev/null +++ b/cpp/src/pass/rulespass.cpp @@ -0,0 +1,14 @@ +#include "rulespass.h" + +RulesPass::RulesPass(const AST& ast) + : __root(ast) +{} + +void +RulesPass::run(ClaspLayer& clasp) +{ + for(const auto& rule: __root.__rules) + { + rule->compile(clasp); + } +} diff --git a/cpp/src/pass/rulespass.h b/cpp/src/pass/rulespass.h new file mode 100644 index 0000000..eb7f8d4 --- /dev/null +++ b/cpp/src/pass/rulespass.h @@ -0,0 +1,16 @@ +#ifndef RULESPASS_H +#define RULESPASS_H +#include +#include + +class RulesPass +{ +public: + RulesPass(const AST& ast); + void run(ClaspLayer& clasp); + +private: + const AST& __root; +}; + +#endif // RULESPASS_H diff --git a/cpp/src/utils.h b/cpp/src/utils.h new file mode 100644 index 0000000..965be11 --- /dev/null +++ b/cpp/src/utils.h @@ -0,0 +1,10 @@ +#ifndef UTILS_H +#define UTILS_H +/* +template +struct DdesctructableClass { + +} +*/ + +#endif // UTILS_H diff --git a/cpp/tests/tests.cpp b/cpp/tests/tests.cpp index 726d165..e3cc471 100644 --- a/cpp/tests/tests.cpp +++ b/cpp/tests/tests.cpp @@ -1,61 +1,89 @@ #include "Scanner.h" #include "Parser.h" #include "ast.h" #include #include -#include "cfgpass.h" +#include "pass/cfgpass.h" +#include "pass/functiontagspass.h" +#include "pass/rulespass.h" + #include "clasplayer.h" -#include "functiontagspass.h" #include using namespace std; void testParser_1() { shared_ptr scanner(new Scanner(L"scripts/input.xreate")); shared_ptr parser(new Parser(scanner.get())); parser->Parse(); flush(cout); AST& root = parser->root; LLVMLayer l; root.compile(l); root.run(l); } void testClasp2() { shared_ptr scanner(new Scanner(L"scripts/input.xreate")); shared_ptr parser(new Parser(scanner.get())); parser->Parse(); flush(cout); if (parser->errors->count) { cout << "Found " << parser->errors->count << " errors. Stop" << endl; exit(1); } AST& root = parser->root; ClaspLayer clasp; FunctionTagsPass(root).run(clasp); + RulesPass(root).run(clasp); + + CFGPass(root).run(clasp); + //clasp.addRule(":- call(X, Y), tag(Y, unsafe), not tag(X, unsafe), function(X), function(Y)."); + + clasp.run(); +} + +void testUnsafeCode1() +{ + shared_ptr scanner(new Scanner(L"scripts/cases/unsafe-code.xreate")); + shared_ptr parser(new Parser(scanner.get())); + parser->Parse(); + flush(cout); + + if (parser->errors->count) + { + cout << "Found " << parser->errors->count << " errors. Stop" << endl; + exit(1); + } + + AST& root = parser->root; + ClaspLayer clasp; + + FunctionTagsPass(root).run(clasp); + RulesPass(root).run(clasp); CFGPass(root).run(clasp); - clasp.addRule(":- call(X, Y), tag(Y, unsafe), not tag(X, unsafe), function(X), function(Y)."); + //clasp.addRule(":- call(X, Y), tag(Y, unsafe), not tag(X, unsafe), function(X), function(Y)."); clasp.run(); } void tests_ListsMultiply() { } int main() { - testClasp2(); + testUnsafeCode1(); return 0; } diff --git a/scripts/cases/unsafe-code.xreate b/scripts/cases/unsafe-code.xreate new file mode 100644 index 0000000..9705f31 --- /dev/null +++ b/scripts/cases/unsafe-code.xreate @@ -0,0 +1,23 @@ + +//unsafe code propagation rule +rule: (X: function, Y: function) +case X call Y + { + warning Y tag unsafe -> X tag unsafe + message "safe function should not call unsafe code" + } + +testfunc3 = function: (a: num, b: num)->num , unsafe //function tags list +{ + x = a+b: num; + y = a - b: num; + + (x + y) / 2; +} + +testfunc2 = function: (a: num)->bool // since testfunc3 marked as unsafe code, + // testfunc2 should be marked so too +{ + b = testfunc3(a+1, a-1): num; + (b==0); +} diff --git a/scripts/input.xreate b/scripts/input.xreate index 22294b3..20c792c 100644 --- a/scripts/input.xreate +++ b/scripts/input.xreate @@ -1,45 +1,42 @@ -/* //unsafe code propagation rule rule: (X: function, Y: function) -case X calls Y +case X call Y { - warning Y has unsafe --> X has unsafe - message format("function A should not call unsafe code B", X as A, Y as B) + warning Y tag unsafe -> X tag unsafe + message "safe function should not call unsafe code" } -*/ - testfunc3 = function: (a: num, b: num)->num , unsafe //function tags list { x = a+b: num; y = a - b: num; (x + y) / 2; } -testfunc2 = function: (a: num)->bool // because of testfunc3 marked as unsafe code, +testfunc2 = function: (a: num)->bool, unsafe // because of testfunc3 marked as unsafe code, // testfunc2 should be marked so too { b = testfunc3(a+1, a-1): num; (b==0); } -/* +/*cl find6 = function: bool { x = [1, 2, 3, 4, 5, 6, 7]: [num]; //эффективная инициализация y = map (x) [el]-> {2 * el}; exists(y, 12); } exists = function: (list:[a], el: a)->bool { acc: bool; acc = reduce(list, false) [el, acc] -> { acc = acc & (el == e): saturated(true); } } */