CC S02E03

March 27, 2013, 8:52 p.m.

Калькулятор с переменными на 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)
)
comments powered by Disqus