Parsing 野牛“噎住了”;“不完整”;表达

Parsing 野牛“噎住了”;“不完整”;表达,parsing,grammar,bison,Parsing,Grammar,Bison,我已经用bison(在yacc模式下)编写了一个解析器,现在我正试图实现某种“不完整”(但有效)的指令 语法有点复杂,但相关部分是: script: input { root = createScript($1); } ; input: statement | input statement { $$ = createSequence($1, $2); } ; statement: S_SEMICOLON { $$ = createNode(); } | declar

我已经用bison(在yacc模式下)编写了一个解析器,现在我正试图实现某种“不完整”(但有效)的指令

语法有点复杂,但相关部分是:

script:
  input { root = createScript($1); }
  ;
input:
  statement
  | input statement { $$ = createSequence($1, $2); }
  ;
statement:
  S_SEMICOLON { $$ = createNode(); }
  | declaration S_SEMICOLON { $$ = $1; }
  | assignment S_SEMICOLON { $$ = $1; }
  | command S_SEMICOLON { $$ = $1; }
  | compound S_SEMICOLON { $$ = $1; }
  | block S_SEMICOLON { $$ = $1; }
  ;
declaration:
  (...)
block:
  S_LTRACKET input S_RTRACKET { createBlock($2); }
  | S_LTRACKET input { createIncomplete($2); }
  ;
(LTRACKET为“{”,RTRACKET为“}”)

由于某些原因,我无法理解第一个“块”被正确解析,而第二个没有。如果我解析“{inti=7;stdout(I);}”,它会工作,但如果我尝试解析“{;”,它会以“语法错误”停止

调试输出没有说明太多:

Starting parse
Entering state 0
Reading a token: Next token is token S_LTRACKET ()
Shifting token S_LTRACKET ()
Entering state 40
Reading a token: Next token is token S_SEMICOLON ()
Shifting token S_SEMICOLON ()
Entering state 38
Reducing stack by rule 9 (line 135):
   $1 = token S_SEMICOLON ()
-> $$ = nterm statement ()
Stack now 0 40
Entering state 54
Reducing stack by rule 2 (line 126):
   $1 = nterm statement ()
-> $$ = nterm input ()
Stack now 0 40
Entering state 78
Reading a token: Now at end of input.
Error: syntax error
Error: popping nterm input ()
Stack now 0 40
Error: popping token S_LTRACKET ()
Stack now 0
Stack now 0

我做错了什么?

问题是您没有处理移位/减少冲突。运行bison时,您会看到如下错误消息:

parser.y: conflicts: 1 shift/reduce 
parser.y:20.5-39: warning: rule useless in parser due to conflicts: block: S_LTRACKET input
如果查看从
bison-v
获得的.output文件,您会看到如下内容:

state 15

    3 input: input . statement
   10 block: S_LTRACKET input . S_RTRACKET
   11      | S_LTRACKET input .

   ';'  [reduce using rule 11 (block)]
支持shift的默认shift/REDUCT分辨率意味着,如果没有右大括号,它将永远不会减少块规则。此冲突源于对输入的歧义,如
{;}
,可以将其解析为包含不完整块的块或包含块的不完整块

现在,您可能会问“为什么在没有额外的大括号集的情况下会发生这种情况?解析器的单标记前瞻应该看到EOF并决定减少而不是移位?”如果您使用LR(1)解析器生成器,事实上就是这样,但是bison(和yacc)使用LALR(1),它结合了仅在前瞻中不同的状态


此外,语法不接受您所说的内容--示例输入
{int i=7;stdout(i);}
将由于缺少最后一个
(在
}
之后)而导致语法错误。您可以更改
语句
规则,以在
之后去掉
,在这种情况下,您将得到更多的移位/减少冲突,但它至少会接受您希望它接受的输入。

您是对的,它不是(但是这种语言有一个简单的预处理器,在交互模式下每行都加上一个分号)部分已经消失,一切都正常了,预处理器还可以检查不平衡的大括号。谢谢。这并不完全正确,因为它值得。你可以告诉bison生成一个规范的LR语法,它将报告完全相同的SR冲突,因为呈现的语法不是LR(k)。问题是一个块,因此是一个不完整的块,后面必须跟一个分号。但是,一个不完整的块需要以分号结尾,因为
input
需要以分号结尾。因此,
{;
不是一个有效的句子。
{;;
是,SR冲突出现在
{;
中的第一个
之后:
{;;};
{;;
都是有效的程序,但在
{;
之后,您无法判断是哪个程序。