概述
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++版实现的全部内容,希望文章能够帮你解决bison(yacc)中关于calc的一个C++版实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复