Калькулятор с переменными на flex, bison и C++
Вы можете скачать все исходники одним архивом.
Makefile
.PHONY: Build Lexer Parser
Build: Parser Lexer
g++ -o compiler lexer.cpp parser.cpp driver.cpp compiler.cpp
Lexer: lexer.l
flex -o lexer.cpp lexer.l
Parser: parser.y
bison -d -o parser.cpp parser.y
compiler.cpp
#include <iostream>
#include "driver.hpp"
int main(int argc, const char* argv[])
{
Driver driver;
for (++argv; argv[0]; ++argv)
if (*argv == std::string ("-p"))
driver.trace_parsing = true;
else if (*argv == std::string ("-s"))
driver.trace_scanning = true;
else if (!driver.parse(*argv))
std::cout << driver.result << std::endl;
return 0;
}
driver.hpp
#ifndef DRIVER_H
#define DRIVER_H
#include <string>
#include <map>
#include "parser.hpp"
#define YY_DECL \
yy::Parser::token_type \
yylex (yy::Parser::semantic_type* yylval, \
Driver& driver)
YY_DECL;
class Driver
{
public:
Driver();
virtual ~Driver();
std::map<std::string, double> variables;
double result;
void scan_begin();
void scan_end();
bool trace_scanning;
int parse(const std::string& filename);
std::string filename;
bool trace_parsing;
void error(const std::string& m);
};
#endif
driver.cpp
#include <iostream>
#include "Driver.hpp"
Driver::Driver() :
trace_scanning(false),
trace_parsing(false)
{
}
Driver::~Driver()
{
}
int Driver::parse(const std::string& filename_)
{
filename = filename_;
scan_begin();
yy::Parser parser(*this);
parser.set_debug_level(trace_parsing);
int res = parser.parse();
scan_end();
return res;
}
void Driver::error(const std::string& m)
{
std::cerr << m << std::endl;
}
parser.y
%skeleton "lalr1.cc"
%require "2.5"
%defines
%define parser_class_name "Parser"
%code requires {
#include <string>
class Driver;
}
%parse-param { Driver& driver }
%lex-param { Driver& driver }
%debug
%error-verbose
%union {
double dval;
std::string* sval;
}
%code {
#include "driver.hpp"
}
%token END 0 "end of file"
%token PLUS
%token MINUS
%token TIMES
%token SLASH
%token LPAREN
%token RPAREN
%token EQUALS
%token SEMICOLON
%token DEF
%token EXTERN
%token <sval> IDENTIFIER "identifier"
%token <dval> NUMBER "number"
%type <dval> expr
%%
%start unit;
unit : assignments expr { driver.result = $2; };
assignments : assignments assignment {}
| /* Nothing. */ {};
assignment : IDENTIFIER EQUALS expr
{ driver.variables[*$1] = $3; delete $1; };
%left PLUS MINUS;
%left TIMES SLASH;
expr : expr PLUS expr { $$ = $1 + $3; }
| expr MINUS expr { $$ = $1 - $3; }
| expr TIMES expr { $$ = $1 * $3; }
| expr SLASH expr { $$ = $1 / $3; }
| IDENTIFIER { $$ = driver.variables[*$1]; delete $1; }
| NUMBER { $$ = $1; };
%%
void yy::Parser::error(const yy::location& l, const std::string& m)
{
driver.error(m);
}
lexer.l
%{
#include <iostream>
#include "parser.hpp"
#include "driver.hpp"
typedef yy::Parser::token token;
#define yyterminate() return token::END
%}
%option noyywrap
%option debug
digit [0-9]
char [a-zA-Z_]
nchar [a-zA-Z_0-9]
ws [ \t]
nl [\r\n]
%%
"+" return token::PLUS;
"-" return token::MINUS;
"*" return token::TIMES;
"/" return token::SLASH;
"(" return token::LPAREN;
")" return token::RPAREN;
"=" return token::EQUALS;
";" return token::SEMICOLON;
"def" return token::DEF;
"extern" return token::EXTERN;
{char}{nchar}* {
yylval->sval = new std::string(yytext);
return token::IDENTIFIER;
}
{digit}*\.?{digit}+ {
yylval->dval = std::atof(yytext);
return token::NUMBER;
}
({nl}|{ws})+ /* ignore */
. { driver.error("invalid character"); }
%%
void Driver::scan_begin()
{
yy_flex_debug = trace_scanning;
if (filename == "-")
yyin = stdin;
else if (!(yyin = fopen (filename.c_str (), "r")))
{
error(std::string ("cannot open ") + filename);
exit(1);
}
}
void Driver::scan_end()
{
fclose(yyin);
}
input.txt
a = 1 + 2 * 3
b = a * 2
a + b
Домашнее задание
Взяв за основу калькулятор на flex + bison + C++, необходимо построить AST, которое затем отобразить в строковом виде. Например, по входу
a = 1 + 2 * 3
b = a * 2
a + b
построить
((= a (+ 1 (* 2 3)))
(= b (* a 2))
(+ a b)
)