Я создал синтаксический анализатор через Flex/Bison, который неожиданно терпит неудачу во время синтаксического анализа. Вот упрощенный пример, который показывает проблему
Лексер.л:
%{
#include "Parser.h"
%}
%option noyywrap nodefault
%%
"foo" { return FOO; }
"bar" { return BAR; }
"(" { return OP; }
")" { return CP; }
[ \t\n]+ { /* DO NOTHING */ }
. { YY_FATAL_ERROR("unknown character"); }
%%
И Parser.y (с включенной трассировкой и многословием):
%{
#include <stdio.h>
int yylex();
void yyerror (char const *s);
%}
%debug
%verbose
%error-verbose
%token FOO BAR OP CP
%%
program_expr : foo_expr bar_expr {}
;
foo_expr : /* NOTHING */ {}
| OP FOO CP {}
;
bar_expr : /* NOTHING */ {}
| OP BAR CP {}
;
%%
int main(int argc, char** argv)
{
yydebug = 1;
yyparse();
return 0;
}
void yyerror (char const *s) { fprintf(stderr, "%s\n", s); }
Но сгенерированный синтаксический анализатор потерпит неудачу, если я укажу ввод, например (bar)
- дерево синтаксического анализа в этом случае должно содержать выражение foo
, которое пусто. Он сообщает:
Starting parse
Entering state 0
Reading a token: Next token is token OP ()
Shifting token OP ()
Entering state 1
Reading a token: Next token is token BAR ()
syntax error, unexpected BAR, expecting FOO
Error: popping token OP ()
Stack now 0
Cleanup: discarding lookahead token BAR ()
Stack now 0
Вот фрагмент текста из сгенерированного описания shift/reduce automata
:
state 0
0 $accept: . program_expr $end
OP shift, and go to state 1
OP [reduce using rule 2 (foo_expr)]
$default reduce using rule 2 (foo_expr)
program_expr go to state 2
foo_expr go to state 3
state 1
3 foo_expr: OP . FOO CP
FOO shift, and go to state 4
state 2
0 $accept: program_expr . $end
$end shift, and go to state 5
state 3
1 program_expr: foo_expr . bar_expr
OP shift, and go to state 6
$default reduce using rule 4 (bar_expr)
bar_expr go to state 7
Но я не могу понять смысл/синтаксис таких состояний. Что не так с моей грамматикой/парсером?
@sepp2k, извини. Решил проверить, вывод есть Parser.y: conflicts: 1 shift/reduce
исправлю этот вопрос
Bison по умолчанию генерирует парсеры LALR(1). LALR(1) означает просмотр вперед 1 токена слева направо синтаксического анализатора.
Ваши грамматики не LALR(1). На ОП не ясно, ожидать ли фу или бар. Это конфликт уменьшения/уменьшения.
Смотри сюда: https://en.wikipedia.org/wiki/LALR_parser
Но обычно Bison может генерировать парсер LR. По крайней мере, запись в вики здесь утверждает, что: https://en.wikipedia.org/wiki/GNU_Bison
Ваш случай - "загадочный конфликт": https://www.gnu.org/software/bison/manual/html_node/Mysterious-Conflicts.html#Mysterious-Conflicts
Это конфликт сдвига/уменьшения, а не конфликт уменьшения/уменьшения: уменьшите пустой foo
или сдвиньте (
, что вызовет foo
. bar
невозможно, пока foo
не будет уменьшено. Таким образом, нет никаких сомнений в том, что foo
требуется; вопрос в том, является ли foo
пустым или нет. Так что этот случай вовсе не "загадочный".
Если вы хотите принять только (bar)
в качестве входных данных, вы можете использовать следующее:
program_expr : foo_expr bar_expr {}
| bar_expr {}
;
вместо этого:
program_expr : foo_expr bar_expr {}
;
Тестовый вывод:
> echo "(bar)" | ./Parser
Starting parse
Entering state 0
Reading a token: Next token is token OP ()
Shifting token OP ()
Entering state 1
Reading a token: Next token is token BAR ()
Shifting token BAR ()
Entering state 6
Reading a token: Next token is token CP ()
Shifting token CP ()
Entering state 11
Reducing stack by rule 6 (line 20):
$1 = token OP ()
$2 = token BAR ()
$3 = token CP ()
-> $$ = nterm bar_expr ()
Stack now 0
Entering state 4
Reducing stack by rule 2 (line 14):
$1 = nterm bar_expr ()
-> $$ = nterm program_expr ()
Stack now 0
Entering state 2
Reading a token: Now at end of input.
....
Но почему этот образец работает? какая разница в грамматике?
Теперь он принимает bar_expr в качестве входных данных. Раньше требовалось foo_expr перед bar_expr.
Обратите внимание, что это на самом деле увеличивает количество конфликтов, если вы не удалите пустую альтернативу из foo_expr
.
Вы уверены, что не получали предупреждений о конфликтах? У меня возникает конфликт, когда я запускаю ваш код через bison, и опубликованный вами подробный вывод содержит сокращение в квадратных скобках, что также указывает на конфликт.