bison(yacc)中关于calc的一个C++版实现

bison中一个calc的C++版实现,区别于传统的C语言实现,我这边整理了一个可编译的版本用以参考

calc++-driver.h

#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

calc++-driver.cpp

#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;
}

calc++-scanner.l

%{ /* -*- 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);
}

calc++-parser.y

%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);
}

main.cpp

#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;
}

最后对应的Makefile

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'

你可能感兴趣的:(bison(yacc)中关于calc的一个C++版实现)