// See https://www.gnu.org/software/bison/manual/html_node/Calc_002b_002b-Parser.html // needed for creation of parser.hh (C++ style) %skeleton "lalr1.cc" /* -*- C++ -*- */ %require "3.5" %defines // In C we would define a %union to use as our symbol type // In C++ and newer bison we can tell it to use a std::variant for a symbol type // We declare our intention to use is with %define api.token.constructor %define api.value.type variant %define parse.assert // and then just use whatever types we want. 'parse.assert' makes sure we use the right types in symbols %code requires { // place your includes here # include class driver; } // The parsing context is passed to flex and bison. // Equivalent to both %lex-param and %parse-param // You could also define 'int yylex (driver& drv)' and 'int yyparse (driver& drv);' %param { driver& drv } //request location tracking // made available by 'yy::location& loc = drv.location;' in scanner.ll %locations %define parse.trace %define parse.error verbose %code { # include "driver.hh" } // DRY - tokens shall begin with "TOK_" %define api.token.prefix {TOK_} %token END 0 "end of file" ASSIGN ":=" MINUS "-" PLUS "+" STAR "*" SLASH "/" LPAREN "(" RPAREN ")" ; // these are our variant types // bison generates functions like // 'symbol_type make_NUMBER (const std::string&, const location_type&);' for us, which can be overwritten %token IDENTIFIER "identifier" %token NUMBER "number" // declare nonterminal expression // compare to %type: https://www.gnu.org/software/bison/manual/html_node/Type-Decl.html // tl;dr: use nterm explicitly for nonterminals, type can also be a terminal // int: an exp resolves/ is evaluated to an int // replace e.g. with AST node types %nterm exp // unit etc. do not need to be declared here, because they have no type to be evaluated to, // as they are evaluated to other nterms %printer { yyo << $$; } <*>; //print values using their operator<< %% %start unit; // this is our root node, so to speak unit: assignments exp { drv.result = $2; }; // here we set the final outcome of exp to the result of our driver // sort of to keep it when the compiling is done // imagine you could also put a pointer to the root node of you ast here :) assignments: %empty {} | assignments assignment {}; assignment: "identifier" ":=" exp { drv.variables[$1] = $3; }; // we save our variable inside the driver because we want to store it for later use // you will find this everytime mathematical expressions are used // it declares operator precedence; see here https://www.gnu.org/software/bison/manual/html_node/Precedence-Decl.html %left "+" "-"; %left "*" "/"; exp: "number" | "identifier" { $$ = drv.variables[$1]; } | exp "+" exp { $$ = $1 + $3; } // these are real mathematical expressions being evaluated and stored back in exp ($$) | exp "-" exp { $$ = $1 - $3; } | exp "*" exp { $$ = $1 * $3; } | exp "/" exp { $$ = $1 / $3; } | "(" exp ")" { $$ = $2; } %% void yy::parser::error (const location_type& l, const std::string& m) { std::cerr << l << ": " << m << '\n'; }