%require "3.4" %define api.pure full %locations %param { class ParserContext *parser } %param { yyscan_t scanner } %code { int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, class ParserContext *parser, yyscan_t scanner); void yyerror(YYLTYPE* yyllocp, class ParserContext *parser, yyscan_t unused, const char* msg); } %{ #include #include "ast/ast.h" #include "Parser.h" typedef void* yyscan_t; %} %union { char cval; int ival; float fval; std::string *sval; StatementNode *stmtNode; StatementBlock *stmtBlock; StatementIfNode *stmtIfNode; StatementFnDeclNode *fnStmtNode; StatementNewNode *stmtNewNode; StatementWhileNode *stmtWhileNode; StatementWithNode *stmtWithNode; StatementSwitchNode *stmtSwitchNode; ExpressionNode *exprNode; ExpressionIdentifierNode *exprIdentNode; ExpressionFnCallNode *exprCallNode; ExpressionInOpNode *exprInNode; ExpressionBinaryOpNode *exprBinaryNode; ExpressionUnaryOpNode *exprUnaryNode; ExpressionListNode *exprListNode; ExpressionPostfixNode *exprPostfix; std::vector *exprList; std::vector *caseNodeList; std::vector *indexList; EnumList *enumList; EnumMember *enumMember; } %token T_INT %token T_FLOAT %token T_IDENTIFIER %token T_STRCONSTANT %token '@' %token '.' ',' ':' ';' '|' %token '(' ')' %token '{' '}' %token '[' ']' %token T_OPTERNARY %token '!' %token T_OPAND T_OPOR %token T_OPEQUALS T_OPNOTEQUALS %token '<' T_OPLESSTHANEQUAL %token '>' T_OPGREATERTHANEQUAL %token '+' '-' '*' '/' '^' '%' '&' %token '=' %token T_OPADDASSIGN T_OPSUBASSIGN T_OPMULASSIGN T_OPDIVASSIGN T_OPPOWASSIGN T_OPMODASSIGN T_OPCATASSIGN %token T_OPDECREMENT T_OPINCREMENT %token T_OPBWXOR T_OPBWLSHIFT T_OPBWRSHIFT T_OPBWINVERT %token T_OPBWORASSIGN T_OPBWANDASSIGN T_OPBWLSHIFTASSIGN T_OPBWRSHIFTASSIGN %token T_KWPUBLIC %token T_KWIF T_KWELSE T_KWELSEIF T_KWFOR T_KWWHILE T_KWBREAK T_KWCONTINUE T_KWRETURN T_KWIN %token T_KWFUNCTION T_KWNEW T_KWWITH T_KWENUM %token T_KWSWITCH T_KWCASE T_KWDEFAULT T_KWCONST %token T_KWCAST_INT T_KWCAST_FLOAT T_KWTRANSLATE %precedence T_KWIF %precedence T_KWELSE T_KWELSEIF %right '=' T_OPADDASSIGN T_OPSUBASSIGN T_OPMULASSIGN T_OPDIVASSIGN T_OPPOWASSIGN T_OPMODASSIGN T_OPCATASSIGN T_OPBWLSHIFTASSIGN T_OPBWRSHIFTASSIGN %left '[' T_OPTERNARY ':' %left T_OPOR %left T_OPAND %left '&' '|' T_OPBWXOR T_OPBWLSHIFT T_OPBWRSHIFT %left '@' %left '<' T_OPLESSTHANEQUAL %left '>' T_OPGREATERTHANEQUAL %left T_OPEQUALS T_OPNOTEQUALS T_KWIN %left '+' '-' %left '*' '/' '^' '%' %right '!' T_OPDECREMENT T_OPINCREMENT T_OPBWINVERT %left '.' %type expr %type constant constant_neg primary %type postfix %type expr_cast %type expr_intconst expr_numberconst expr_strconst %type expr_assignment expr_new %type expr_ident %type expr_ops_unary %type expr_ops_binary expr_ops_comparison %type expr_ops_in %type expr_arraylist %type stmt_if stmt_if_extension %type stmt_list %type stmt %type stmt_ret stmt_break stmt_continue stmt_expr %type stmt_block %type stmt_new %type stmt_for %type stmt_while %type stmt_with %type expr_list expr_list_with_empty %type decl_list %type decl %type stmt_fndecl %type expr_functionobj %type stmt_switch %type stmt_caseblock_list %type enum_list %type enum_item %type array_idx %type array_idx_list // Destructors for allocated std vectors incase an error happens // during parsing before ownership is moved to the node %destructor { delete $$; } %destructor { delete $$; } %destructor { delete $$; } %start program %% program: decl_list { parser->setRootStatement($1); } //| decl_list error { parser->setRootStatement(nullptr); printf("Detected error at root\n"); yyerrok; } ; decl_list: { $$ = parser->alloc(); } | decl_list decl { $1->append($2); } ; decl: stmt { $$ = $1; } | stmt_fndecl { $$ = $1; } | decl_const { $$ = nullptr; } | decl_enum { $$ = nullptr; } ; decl_const: T_KWCONST T_IDENTIFIER '=' constant ';' { parser->addConstant(*$2, $4); } | T_KWCONST T_IDENTIFIER '=' constant_neg ';' { parser->addConstant(*$2, $4); } | T_KWCONST T_IDENTIFIER '=' expr_ident ';' { parser->addConstant(*$2, $4); } decl_enum: T_KWENUM '{' enum_list '}' { parser->addEnum($3); } | T_KWENUM T_IDENTIFIER '{' enum_list '}' { parser->addEnum($4, *$2); } ; enum_list: enum_item { $$ = new EnumList($1); } | enum_list ',' enum_item { $$ = $1; $1->addMember($3); } | enum_list error ',' { $$ = $1; parser->addParserError("missing comma in enum list"); } ; enum_item: T_IDENTIFIER { $$ = new EnumMember($1); } | T_IDENTIFIER '=' T_INT { $$ = new EnumMember($1, $3); } | T_IDENTIFIER '=' '-' T_INT { $$ = new EnumMember($1, -$4); } ; stmt_list: stmt { $$ = parser->alloc(); $$->append($1); } | stmt_list stmt { $1->append($2); } ; stmt_block: '{' stmt_list '}' { $$ = $2; } | '{' '}' { $$ = parser->alloc(); } ; stmt: stmt_if { $$ = $1;} | stmt_ret | stmt_break | stmt_continue | stmt_expr | stmt_block { $$ = $1; } | stmt_new { $$ = $1; } | stmt_for { $$ = $1; } | stmt_with { $$ = $1; } | stmt_while { $$ = $1; } | stmt_switch { $$ = $1; } ; stmt_if: T_KWIF stmt_if_extension { $$ = $2; } ; stmt_if_extension: '(' expr ')' stmt %prec T_KWIF { $$ = parser->alloc($2, $4); } | '(' expr ')' stmt T_KWELSE stmt { $$ = parser->alloc($2, $4, $6); } | '(' expr ')' stmt T_KWELSEIF stmt_if_extension { $$ = parser->alloc($2, $4, $6); } ; stmt_expr: ';' { $$ = 0; } | expr ';' { $$ = $1; } | expr error ';' { $$ = $1; parser->addParserError("missing semicolon"); } ; stmt_new: T_KWNEW T_IDENTIFIER '(' expr_list_with_empty ')' stmt_block { $$ = parser->alloc($2, $4, $6); } ; stmt_for: T_KWFOR '(' expr ';' expr ';' expr ')' stmt { $$ = parser->alloc($3, $5, $7, $9); } | T_KWFOR '(' ';' expr ';' expr ')' stmt { $$ = parser->alloc(nullptr, $4, $6, $8); } | T_KWFOR '(' expr ':' expr ')' stmt { $$ = parser->alloc($3, $5, $7); } ; stmt_while: T_KWWHILE '(' expr ')' stmt { $$ = parser->alloc($3, $5); } ; stmt_with: T_KWWITH '(' expr ')' stmt { $$ = parser->alloc($3, $5); } ; stmt_break: T_KWBREAK ';' { $$ = parser->alloc(); } ; stmt_continue: T_KWCONTINUE ';' { $$ = parser->alloc(); } ; stmt_ret: T_KWRETURN expr ';' { $$ = parser->alloc($2); } | T_KWRETURN ';' { $$ = parser->alloc(nullptr); } | T_KWRETURN error '\n' { parser->addParserError("missing semicolon"); $$ = parser->alloc(nullptr); yyerrok; } ; stmt_switch: T_KWSWITCH '(' expr ')' '{' stmt_caseblock_list '}' { $$ = parser->alloc($3, $6); } ; stmt_caseblock_list: stmt_caseblock_list stmt_caseblock { $$ = $1; $$->push_back(parser->popCaseExpr()); } | stmt_caseblock { $$ = new std::vector(); $$->push_back(parser->popCaseExpr()); } ; stmt_case_options: stmt_list { parser->setCaseStatement($1); } | stmt_caseblock ; stmt_caseblock: T_KWCASE expr ':' stmt_case_options { parser->pushCaseExpr($2); } | T_KWDEFAULT ':' stmt_case_options { parser->pushCaseExpr(nullptr); } ; stmt_fndecl: T_KWFUNCTION T_IDENTIFIER '(' expr_list_with_empty ')' stmt { $$ = parser->alloc($2, $4, parser->alloc($6)); } | T_KWFUNCTION T_IDENTIFIER '.' T_IDENTIFIER '(' expr_list_with_empty ')' stmt { $$ = parser->alloc($4, $6, parser->alloc($8), $2); } | T_KWPUBLIC stmt_fndecl { $$ = $2; $$->setPublic(true); } ; expr_list_with_empty: { $$ = nullptr; } | expr_list { $$ = $1; } ; expr_list: expr_list ',' expr { $1->push_back($3); } | expr_list ',' expr_functionobj { $1->push_back($3); } | expr { $$ = new std::vector(); $$->reserve(12); $$->push_back($1); } | expr_functionobj { $$ = new std::vector(); $$->reserve(12); $$->push_back($1); } ; constant: expr_intconst | expr_numberconst | expr_strconst ; constant_neg: '-' T_INT { $$ = parser->alloc(-$2); } | '-' T_FLOAT { $$ = parser->alloc($2); } ; primary: constant { $$ = $1;} | expr_ident { $$ = $1; } | '(' expr ')' { $$ = $2; } ; postfix: primary { $$ = parser->alloc($1); } | postfix '[' expr_list ']' { $1->addNode(parser->alloc($3)); } | postfix '(' expr_list_with_empty ')' { // remove last element, to be used as function ident auto funcNode = $1->nodes.back(); $1->nodes.pop_back(); // if we still have nodes, this is used as the object parameter // for the function call. ast::checkPostfixNode will pull out // the underlying node if its the only node in the container ExpressionNode *objectNode = nullptr; if (!$1->nodes.empty()) objectNode = ast::checkPostfixNode($1); // create function node auto n = parser->alloc(funcNode, objectNode, $3); $$ = parser->alloc(n); } | postfix '.' primary { $1->addNode($3); } ; expr: postfix { $$ = ast::checkPostfixNode($1); } | expr_cast { $$ = $1; } | expr_arraylist { $$ = $1; } | expr_ops_binary { $$ = $1; } | expr_ops_unary { $$ = $1; } | expr_ops_comparison { $$ = $1; } | expr_ops_in { $$ = $1; } | expr T_OPTERNARY expr ':' expr { $$ = parser->alloc($1, $3, $5); } ; expr_ops_unary: '-' expr { $$ = parser->alloc($2, ExpressionOp::UnaryMinus, true); } | '@' expr { $$ = parser->alloc($2, ExpressionOp::UnaryStringCast, true); } | '!' expr { $$ = parser->alloc($2, ExpressionOp::UnaryNot, true); } | T_OPBWINVERT expr { $$ = parser->alloc($2, ExpressionOp::BitwiseInvert, true); } | T_OPDECREMENT expr { $$ = parser->alloc($2, ExpressionOp::Decrement, true); } | T_OPINCREMENT expr { $$ = parser->alloc($2, ExpressionOp::Increment, true); } | expr T_OPINCREMENT { $$ = parser->alloc($1, ExpressionOp::Increment, false); } | expr T_OPDECREMENT { $$ = parser->alloc($1, ExpressionOp::Decrement, false); } ; expr_ops_binary: expr '+' expr { $$ = parser->alloc($1, $3, ExpressionOp::Plus); } | expr '-' expr { $$ = parser->alloc($1, $3, ExpressionOp::Minus); } | expr '*' expr { $$ = parser->alloc($1, $3, ExpressionOp::Multiply); } | expr '/' expr { $$ = parser->alloc($1, $3, ExpressionOp::Divide); } | expr '%' expr { $$ = parser->alloc($1, $3, ExpressionOp::Mod); } | expr '^' expr { $$ = parser->alloc($1, $3, ExpressionOp::Pow); } | expr '&' expr { $$ = parser->alloc($1, $3, ExpressionOp::BitwiseAnd); } | expr '|' expr { $$ = parser->alloc($1, $3, ExpressionOp::BitwiseOr); } | expr T_OPBWXOR expr { $$ = parser->alloc($1, $3, ExpressionOp::BitwiseXor); } | expr T_OPBWLSHIFT expr { $$ = parser->alloc($1, $3, ExpressionOp::BitwiseLeftShift); } | expr T_OPBWRSHIFT expr { $$ = parser->alloc($1, $3, ExpressionOp::BitwiseRightShift); } | expr '=' expr { $$ = parser->alloc($1, $3, ExpressionOp::Assign, true); } | expr '=' expr_assignment { $$ = parser->alloc($1, $3, ExpressionOp::Assign, true); } | expr '@' expr { $$ = parser->alloc($1, $3, $2); } | expr T_OPADDASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::PlusAssign, true); } | expr T_OPSUBASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::MinusAssign, true); } | expr T_OPMULASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::MultiplyAssign, true); } | expr T_OPDIVASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::DivideAssign, true); } | expr T_OPPOWASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::PowAssign, true); } | expr T_OPMODASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::ModAssign, true); } | expr T_OPBWLSHIFTASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::BitwiseLeftShiftAssign, true); } | expr T_OPBWRSHIFTASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::BitwiseRightShiftAssign, true); } | expr T_OPCATASSIGN expr { $$ = parser->alloc($1, $3, ExpressionOp::ConcatAssign, true); } ; expr_assignment: expr_new { $$ = $1; } | expr_functionobj { $$ = $1; } | '{' '}' { $$ = parser->alloc(nullptr); } ; expr_functionobj: T_KWFUNCTION '(' expr_list_with_empty ')' stmt { $$ = parser->alloc(parser->generateLambdaFuncName(), $3, parser->alloc($5)); } ; expr_ops_comparison: expr T_OPEQUALS expr { $$ = parser->alloc($1, $3, ExpressionOp::Equal); } | expr T_OPNOTEQUALS expr { $$ = parser->alloc($1, $3, ExpressionOp::NotEqual); } | expr '<' expr { $$ = parser->alloc($1, $3, ExpressionOp::LessThan); } | expr '>' expr { $$ = parser->alloc($1, $3, ExpressionOp::GreaterThan); } | expr T_OPLESSTHANEQUAL expr { $$ = parser->alloc($1, $3, ExpressionOp::LessThanOrEqual); } | expr T_OPGREATERTHANEQUAL expr { $$ = parser->alloc($1, $3, ExpressionOp::GreaterThanOrEqual); } | expr T_OPAND expr { $$ = parser->alloc($1, $3, ExpressionOp::LogicalAnd); } | expr T_OPOR expr { $$ = parser->alloc($1, $3, ExpressionOp::LogicalOr); } ; expr_ops_in: expr T_KWIN '|' expr ',' expr '|' { $$ = parser->alloc($1, $4, $6); } | expr T_KWIN '<' expr ',' expr '>' { $$ = parser->alloc($1, $4, $6); } | expr T_KWIN expr { $$ = parser->alloc($1, $3, nullptr); } ; expr_ident: T_IDENTIFIER { $$ = parser->alloc($1); } ; expr_intconst: T_INT { $$ = parser->alloc($1); } ; expr_numberconst: T_FLOAT { $$ = parser->alloc($1); } ; expr_strconst: T_STRCONSTANT { $$ = parser->alloc($1); } ; expr_arraylist: '{' expr_list '}' { $$ = parser->alloc($2); } | '{' expr_list ',' '}' { $$ = parser->alloc($2); } ; expr_cast: T_KWCAST_INT '(' expr ')' { $$ = parser->alloc($3, ExpressionCastNode::CastType::INTEGER); } | T_KWCAST_FLOAT '(' expr ')' { $$ = parser->alloc($3, ExpressionCastNode::CastType::FLOAT); } | T_KWTRANSLATE '(' expr ')' { $$ = parser->alloc($3, ExpressionCastNode::CastType::TRANSLATION); } ; array_idx: '[' T_INT ']' { $$ = $2; } ; array_idx_list: array_idx_list array_idx { $$ = $1; $$->push_back($2); } | array_idx { $$ = new std::vector(); $$->push_back($1); } ; expr_new: T_KWNEW expr_ident '(' expr_list_with_empty ')' { $$ = parser->alloc($2, $4); } | T_KWNEW array_idx_list { $$ = parser->alloc($2); } ; %% #include "lex.yy.h" void yyerror(YYLTYPE* yyllocp, class ParserContext *parser, yyscan_t unused, const char* s) { /* std::string msg; msg .append("Eeeee Syntax error occurred (line ") .append(std::to_string(parser->lineNumber)) .append(", col ") .append(std::to_string(parser->columnNumber)) .append("): ") .append(s); parser->addParserError(msg); */ // Unset the root statement to indicate failure, if none of our // error catching rules catch the error then a generic error // response will be emitted with the last known line/col number parser->setRootStatement(nullptr); }