Next: Calc++ Scanner, Previous: Calc++ Parsing Driver, Up: A Complete C++ Example [Contents][Index]
The grammar file calc++-parser.yy starts by asking for the C++ deterministic parser skeleton, the creation of the parser header file, and specifies the name of the parser class. Because the C++ skeleton changed several times, it is safer to require the version you designed the grammar for.
%skeleton "lalr1.cc" /* -*- C++ -*- */ %require "2.7.12-4996" %defines %define parser_class_name "calcxx_parser"
Then come the declarations/inclusions needed to define the
%union
. Because the parser uses the parsing driver and
reciprocally, both cannot include the header of the other. Because the
driver’s header needs detailed knowledge about the parser class (in
particular its inner types), it is the parser’s header which will simply
use a forward declaration of the driver.
See %code Summary.
%code requires { # include <string> class calcxx_driver; }
The driver is passed by reference to the parser and to the scanner. This provides a simple but effective pure interface, not relying on global variables.
// The parsing context. %parse-param { calcxx_driver& driver } %lex-param { calcxx_driver& driver }
Then we request the location tracking feature, and initialize the first location’s file name. Afterward new locations are computed relatively to the previous locations: the file name will be automatically propagated.
%locations %initial-action { // Initialize the initial location. @$.begin.filename = @$.end.filename = &driver.file; };
Use the two following directives to enable parser tracing and verbose error messages. However, verbose error messages can contain incorrect information (see LAC).
%debug %error-verbose
Semantic values cannot use “real” objects, but only pointers to them.
// Symbols. %union { int ival; std::string *sval; };
The code between ‘%code {’ and ‘}’ is output in the *.cc file; it needs detailed knowledge about the driver.
%code { # include "calc++-driver.hh" }
The token numbered as 0 corresponds to end of file; the following line
allows for nicer error messages referring to “end of file” instead
of “$end”. Similarly user friendly named are provided for each
symbol. Note that the tokens names are prefixed by TOKEN_
to
avoid name clashes.
%token END 0 "end of file" %token ASSIGN ":=" %token <sval> IDENTIFIER "identifier" %token <ival> NUMBER "number" %type <ival> exp
To enable memory deallocation during error recovery, use
%destructor
.
%printer { yyoutput << *$$; } "identifier" %destructor { delete $$; } "identifier" %printer { yyoutput << $$; } <ival>
The grammar itself is straightforward.
%% %start unit; unit: assignments exp { driver.result = $2; }; assignments: /* Nothing. */ {} | assignments assignment {}; assignment: "identifier" ":=" exp { driver.variables[*$1] = $3; delete $1; }; %left '+' '-'; %left '*' '/'; exp: exp '+' exp { $$ = $1 + $3; } | exp '-' exp { $$ = $1 - $3; } | exp '*' exp { $$ = $1 * $3; } | exp '/' exp { $$ = $1 / $3; } | "identifier" { $$ = driver.variables[*$1]; delete $1; } | "number" { $$ = $1; }; %%
Finally the error
member function registers the errors to the
driver.
void yy::calcxx_parser::error (const yy::calcxx_parser::location_type& l, const std::string& m) { driver.error (l, m); }
Next: Calc++ Scanner, Previous: Calc++ Parsing Driver, Up: A Complete C++ Example [Contents][Index]