Парсер на flex+bison+C++ (странные глюки)

Модератор: Модераторы разделов

frp
Сообщения: 1445
ОС: Debian Squeeze

Парсер на flex+bison+C++

Сообщение frp »

Пишу парсер для мат. выражений (тренируюсь пока). Мой код:
expr.y (bison):

Код: Выделить всё

%{
    #include <iostream>
    #include <stdlib.h>
    #include <string>
    using std::string;
    using std::cout;
    //int yylex(){return getc(stdin);}
    extern int yylex();
    extern int yylineno;
    void yyerror(char *s)
    {
        std::cerr << s << ", line " << yylineno << std::endl;
        exit(1);
    }
    class tree_node //abstract
    {
        public:
            virtual int eval()=0;
    };
    class value: public tree_node
    {
            int num;
        public:
            value(int n){num=n;}
            value(string s)
            {
                const char* c=s.c_str();
                num=atoi(c);
            }
            int eval(){return num;}
    };
    typedef int (*bin_func) (int, int);
    class binary: public tree_node
    {
            tree_node* left,*right;
            bin_func func;
        public:
            binary(tree_node* l,tree_node* r,bin_func f){left=l;right=r;func=f;}
            int eval()
            {
                return func(left->eval(),right->eval());
            }
    };
    int _add(int a,int b){return a+b;}
    int _sub(int a,int b){return a-b;}
    int _mul(int a,int b){return a*b;}
    int _div(int a,int b){return a/b;}
    bin_func get_f(char c){return c=='+'?_add:c=='-'?_sub:c=='*'?_mul:_div;}
    typedef struct {
        string str;
        tree_node* node;
    } YYSTYPE;
    #define YYSTYPE YYSTYPE

%}

%token NUM

%type<str> NUM;
%type<node> EXPR TERM VAL;

%%
EVALUATE: EXPR '\n' {cout<<$1->eval()<<'\n';}
;
EXPR: TERM
    | TERM '+' EXPR {$$=new binary($1,$3,get_f('+'));}
    | TERM '-' EXPR {$$=new binary($1,$3,get_f('-'));}
;
TERM: VAL
    | TERM '*' VAL {$$=new binary($1,$3,get_f('*'));}
    | TERM '/' VAL {$$=new binary($1,$3,get_f('/'));}
;
VAL: NUM {$$=new value($1);}
;
%%
int main(){return yyparse();}

(извиняюсь за деревья разбора, функции _add, _sub, _mul, _div - я в курсе что можно считать прямо во время разбора - у данного поделия в будущем появятся возможности, для которых дерево необходимо).
expr.lex (flex):

Код: Выделить всё

%{
    #include <stdio.h>
    #include <string>
    #define YYSTYPE std::string
    #include "expr.tab.h"
    void yyerror(char* s);
%}
%option yylineno
%option noyywrap
%%
[0-9]+        {yylval=yytext;return NUM;}
[\+\-\*\/\n]    return *yytext;
%%

Инструкция как собрать:
bison -d expr.y
flex expr.lex
g++ lex.yy.c expr.tab.c
Что должно получится:
Программа читает выражения и вычисляет их по очереди. Выражения разделяются переводами строк.
Что получилось:
Программа успешно обрабатывает первое выражение, но на второе обязательно выдает ошибку. В чем причина?

PS. Еще раз извиняюсь за толстый код.
PPS. С bison и flex работаю впервые.
Спасибо сказали:
Аватара пользователя
kosmonaFFFt
Сообщения: 183
ОС: win 7, Kubuntu 10.10

Re: Парсер на flex+bison+C++

Сообщение kosmonaFFFt »

У тебя грамматика может парсить только 1 EVALUATE, поэтому и выдает ошибку...
Попробуй так:

Код: Выделить всё

EVALUATE: EXPR '\n' tail
       ;

tail : /* EMPTY */
     | EXPR 'n' EVALUATE
    ;
Спасибо сказали:
frp
Сообщения: 1445
ОС: Debian Squeeze

Re: Парсер на flex+bison+C++

Сообщение frp »

Сделал. Теперь файл бизона:

Код: Выделить всё

%{
    #include <iostream>
    #include <stdlib.h>
    #include <string>
    using std::string;
    using std::cout;
    //int yylex(){return getc(stdin);}
    extern int yylex();
    extern int yylineno;
    void yyerror(char *s)
    {
        std::cerr << s << ", line " << yylineno << std::endl;
        exit(1);
    }
    class tree_node //abstract
    {
        public:
            virtual int eval()=0;
    };
    class value: public tree_node
    {
            int num;
        public:
            value(int n){num=n;}
            value(string s)
            {
                const char* c=s.c_str();
                num=atoi(c);
            }
            int eval(){return num;}
    };
    typedef int (*bin_func) (int, int);
    class binary: public tree_node
    {
            tree_node* left,*right;
            bin_func func;
        public:
            binary(tree_node* l,tree_node* r,bin_func f){left=l;right=r;func=f;}
            int eval()
            {
                return func(left->eval(),right->eval());
            }
    };
    int _add(int a,int b){return a+b;}
    int _sub(int a,int b){return a-b;}
    int _mul(int a,int b){return a*b;}
    int _div(int a,int b){return a/b;}
    bin_func get_f(char c){return c=='+'?_add:c=='-'?_sub:c=='*'?_mul:_div;}
    typedef struct {
        string str;
        tree_node* node;
    } YYSTYPE;
    #define YYSTYPE YYSTYPE

%}

%token NUM

%type<str> NUM;
%type<node> EXPR TERM VAL;

%%
EVALUATE: EXPR '\n' tail {cout<<$1->eval()<<'\n';}
;
tail:
    | EXPR '\n' EVALUATE {cout<<$1->eval()<<'\n';}
EXPR: TERM
    | TERM '+' EXPR {$$=new binary($1,$3,get_f('+'));}
    | TERM '-' EXPR {$$=new binary($1,$3,get_f('-'));}
;
TERM: VAL
    | TERM '*' VAL {$$=new binary($1,$3,get_f('*'));}
    | TERM '/' VAL {$$=new binary($1,$3,get_f('/'));}
;
VAL: NUM {$$=new value($1);}
;
%%
int main(){return yyparse();}

Теперь программа сначала считывает все выражения и выводит результаты в обратном порядке (хотя обратный порядок легко лечится, если не сразу выводить, а складывать в список (std::list), а потом выводить).
А нужно было чтобы она считывала выражение и сразу выводила результат. Или бизон своими средствами это не умеет?
Спасибо сказали:
frp
Сообщения: 1445
ОС: Debian Squeeze

Re: Парсер на flex+bison+C++

Сообщение frp »

Обошел ограничение средствами lex - при встрече \n возвращаю EOF. Теперь правило EVALUATE имеет вид:

Код: Выделить всё

EVALUATE: EXPR;

Функция main имеет вид:

Код: Выделить всё

int main()
{
    while(true)yyparse();
    return 0;
}
Спасибо сказали: