bison中一个calc的C++版实现,区别于传统的C语言实现,我这边整理了一个可编译的版本用以参考
#ifndef CALCXX_DRIVER_HH # define CALCXX_DRIVER_HH # include <string> # include <map> # include "calc++-parser.h" // Tell Flex the lexer's prototype ... # define YY_DECL \ yy::calcxx_parser::token_type \ yylex (yy::calcxx_parser::semantic_type* yylval, \ yy::calcxx_parser::location_type* yylloc, \ calcxx_driver& driver) // ... and declare it for the parser's sake. YY_DECL; // Conducting the whole scanning and parsing of Calc++. class calcxx_driver { public: calcxx_driver (); virtual ~calcxx_driver (); std::map<std::string, int> variables; int result; // Handling the scanner. void scan_begin (); void scan_end (); bool trace_scanning; // Run the parser. Return 0 on success. int parse (const std::string& f); std::string file; bool trace_parsing; // Error handling. void error (const yy::location& l, const std::string& m); void error (const std::string& m); }; #endif // ! CALCXX_DRIVER_HH
#include "calc++-driver.h" #include "calc++-parser.h" calcxx_driver::calcxx_driver () : trace_scanning (false), trace_parsing (false) { variables["one"] = 1; variables["two"] = 2; } calcxx_driver::~calcxx_driver () { } int calcxx_driver::parse (const std::string &f) { file = f; scan_begin (); yy::calcxx_parser parser (*this); parser.set_debug_level (trace_parsing); int res = parser.parse (); scan_end (); return res; } void calcxx_driver::error (const yy::location& l, const std::string& m) { std::cerr << l << ": " << m << std::endl; } void calcxx_driver::error (const std::string& m) { std::cerr << m << std::endl; }
%{ /* -*- C++ -*- */ # include <cstdlib> # include <cerrno> # include <climits> # include <string> # include "calc++-driver.h" # include "calc++-parser.h" /* Work around an incompatibility in flex (at least versions 2.5.31 through 2.5.33): it generates code that does not conform to C89. See Debian bug 333231 <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>. */ # undef yywrap # define yywrap() 1 /* By default yylex returns int, we use token_type. Unfortunately yyterminate by default returns 0, which is not of token_type. */ #define yyterminate() return token::END %} %option noyywrap nounput batch debug nounistd never-interactive id [a-zA-Z][a-zA-Z_0-9]* int [0-9]+ blank [ \t] %{ # define YY_USER_ACTION yylloc->columns (yyleng); %} %% %{ yylloc->step (); %} {blank}+ yylloc->step (); [\n]+ yylloc->lines (yyleng); yylloc->step (); %{ typedef yy::calcxx_parser::token token; %} /* Convert ints to the actual type of tokens. */ [-+*/] return yy::calcxx_parser::token_type (yytext[0]); ":=" return token::ASSIGN; {int} { errno = 0; long n = strtol (yytext, NULL, 10); if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE)) driver.error (*yylloc, "integer is out of range"); yylval->ival = n; return token::NUMBER; } {id} { yylval->sval = new std::string (yytext); return token::IDENTIFIER; } . driver.error (*yylloc, "invalid character"); %% void calcxx_driver::scan_begin () { yy_flex_debug = trace_scanning; if (file.empty () || file == "-") yyin = stdin; else if (!(yyin = fopen (file.c_str (), "r"))) { error ("cannot open " + file + ": " + strerror(errno)); exit (EXIT_FAILURE); } } void calcxx_driver::scan_end () { fclose (yyin); }
%skeleton "lalr1.cc" /* -*- C++ -*- */ //%require "2.7" %defines %define parser_class_name "calcxx_parser" %code requires { # include <string> class calcxx_driver; } // The parsing context. %parse-param { calcxx_driver& driver } %lex-param { calcxx_driver& driver } %locations %initial-action { // Initialize the initial location. @$.begin.filename = @$.end.filename = &driver.file; }; %debug %error-verbose // Symbols. %union { int ival; std::string *sval; }; %code { # include "calc++-driver.h" } %token END 0 "end of file" %token ASSIGN ":=" %token <sval> IDENTIFIER "identifier" %token <ival> NUMBER "number" %type <ival> exp %printer { std::cout << *$$; } "identifier" %destructor { delete $$; } "identifier" %printer { std::cout << $$; } <ival> %% %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; }; %% void yy::calcxx_parser::error (const yy::calcxx_parser::location_type& l, const std::string& m) { driver.error (l, m); }
#include <iostream> #include "calc++-driver.h" int main (int argc, char *argv[]) { calcxx_driver driver; for (int i = 1; i < argc; ++i) { if (argv[i] == std::string ("-p")) driver.trace_parsing = true; else if (argv[i] == std::string ("-s")) driver.trace_scanning = true; else if (!driver.parse (argv[i])) std::cout << driver.result << std::endl; } return 0; }
all: calc++.exe calc++.exe: calc++-driver.o calc++-parser.o calc++-scanner.o main.o g++ -o calc++.exe calc++-driver.o calc++-parser.o calc++-scanner.o main.o calc++-driver.o: calc++-driver.cpp calc++-driver.h calc++-parser.h g++ -c calc++-driver.cpp calc++-parser.o: calc++-parser.cpp calc++-parser.h calc++-driver.h g++ -c calc++-parser.cpp calc++-parser.cpp calc++-parser.h: calc++-parser.y bison --defines=calc++-parser.h -ocalc++-parser.cpp calc++-parser.y calc++-scanner.o: calc++-scanner.cpp calc++-parser.h calc++-driver.h g++ -c calc++-scanner.cpp calc++-scanner.cpp: calc++-scanner.l flex -ocalc++-scanner.cpp calc++-scanner.l main.o: .PHONY: clean clean: -rm *.o calc++-parser.h calc++-parser.cpp calc++-scanner.cpp location.hh position.hh stack.hh calc++.exe
注意:在lexer文件中有这样的选项
%option .. nounistd never-interactive
其目的是避免在VC++中出现的编译错误 Cannot open include file: 'unistd.h'