Калькулятор парсера бизонов

0

это мой код бизона:

%}

%union  {   
int    int_val;
}


%left '+' '-'
%nonassoc '(' ')'

%token INTEGER PRINT 
%type <int_val> expr_int INTEGER

%%
program: command '\n'   { return 0; }
        ;
command: print_expr
        ;
print_expr: PRINT expr_int          { cout<<$2<<endl; }
expr_int: expr_int '+' expr_int     { $$ = $1 + $3; }
        | expr_int '-' expr_int     { $$ = $1 - $3; }
        | '(' expr_int ')'                  { $$ = $2; }
        | INTEGER
        ;

и это гибкий код:

%}

INTEGER     [1-9][0-9]*|0
BINARY      [-+]
WS              [ \t]+
BRACKET     [\(\)]

%%
print{WS}               { return PRINT; }
{INTEGER}               {   yylval.int_val=atoi(yytext); return INTEGER; }
{BINARY}|\n     {   return *yytext; }
{BRACKET}               {   return *yytext; }
{WS}                    {}
.                       { return *yytext; }

%%

//////////////////////////////////////////////////
int yywrap(void) { return 1;  }  // Callback at end of file

Недействительными входами для программы являются:

print 5

вывод:

5

вход:

print (1+1)

вывод:

2

Но по какой-то причине для следующих входов я не получаю немедленную ошибку:

print (1+1))

вывод:

2
some error

вход:

print 5!

вывод:

5
some error

Я хотел бы, чтобы ошибка была напечатана немедленно, без выполнения команды печати и затем выброса ошибки.

Как мне изменить программу, чтобы она не печатала ошибочные входы?

  • 0
    Компилятор наказывает вас за то, что вы называете скобки "скобками".
Теги:
bison
flex-lexer

4 ответа

1

Загрузите книгу "flex & bison" Джона Левина или руководство "зубров" от gnu. Оба содержат калькулятор инфикс, который вы можете ссылаться.

Грамматика, которую вы написали "'(' expr_int ')'", сводится к expr_int до того, как будет обнаружена грамматически некорректная ')' in '(1 + 1)). Это синтаксический анализатор:

(1 + 1)) => ( expr_int )) => expr_int) 

и затем видит ошибку. Чтобы зафиксировать ошибку, вы должны изменить синтаксический анализатор, чтобы увидеть ошибку до сокращения, и вам нужно сделать это для всех ошибок, которые вы хотите обработать. Поэтому вы должны написать (в данном случае):

expr_int '(' expr_int ')' ')' {это сообщение об ошибке}

Короткий ответ после долгого ответа заключается в том, что создавать парсер, содержащий экземпляры всех возможных ошибок, нецелесообразно. То, что у вас есть, хорошо для того, что вы делаете. То, что вы должны исследовать, - это как (изящно) восстановить от ошибки, а не отказаться от синтаксического анализа.

Ваши "программные" и "командные" нетерминалы можно комбинировать как:

program: print-expr '\n' { return 0; }

В отдельном примечании ваши регулярные выражения могут быть переписаны с хорошим эффектом:

%%
INTEGER     [0-9]+
WS          [ \t]+

%%
print/{WS}              { return PRINT; }
{INTEGER}               { yylval.int_val=atoi(yytext); return INTEGER; }
'('                     { return '(';     }
')'                     { return ')';     }
'+'                     { return '+';     }
'-'                     { return '-';     }
{WS}*                   {}
\n                      { return '\n';    }
.                       { return *yytext; }  // do you really want to do this?

%%
0

В yacc/bison код, связанный с семантическим действием, выполняется, как только это правило уменьшается, что может произойти сразу же после того, как токены для правила были сдвинуты, прежде чем смотреть на любой следующий контекст, чтобы увидеть, есть ли там и ошибка или нет (так называемое сокращение по умолчанию), используемое для уменьшения таблиц разбора).

Если вы хотите избежать печати ответа до тех пор, пока вся строка не будет прочитана (и не признана), вам необходимо включить новую строку в правило, в котором есть действие, которое печатает сообщение. В вашем случае вы можете переместить новую print_expr правила program правило print_expr:

program: command   { return 0; } ;
print_expr: PRINT expr_int '\n' { cout<<$2<<endl; }

Конечно, это все равно даст вам ошибку (после печати), если вы дадите ей несколько строк ввода.

  • 0
    return 0 часть действительно не рекомендуется. В этом случае это бесполезно, а в противном случае лучше использовать YYABORT .
0

Ну, это потому, что вы выполняете код во время его разбора. Хороший старый зубной калькулятор предназначен, чтобы научить вас писать грамматику, а не реализовывать полный компилятор/интерпретатор.

Обычный способ создания компилятора/интерпретатора следующий:

lexer -> parser -> semantic analyser -> code generator -> interpreter

Предоставленное здание полноправного компилятора может быть излишним в вашем случае. То, что вам нужно, это хранить результат где-то и выводить его только после того, как yyparse вернулся без ошибок.

0

Создайте токен конца строки (например ;) для вашего языка и сделайте все строки операторами точно в тот момент, когда они столкнутся с этим токеном конца строки.

  • 0
    Я действительно не понимаю, что делать .., вы можете скопировать и исправить мою код?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню