野牛解析器窒息“不完整”的表达

时间:2013-12-02 17:36:31

标签: parsing grammar 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为'}')

由于某种原因,我无法理解第一个“块”被正确解析,第二个没有。如果我解析“{int i = 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

我做错了什么?

1 个答案:

答案 0 :(得分:1)

问题是你有一个你没有处理的转移/减少冲突。当您运行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 / reduce分辨率意味着它不会在没有右括号的情况下减少块规则。这种冲突来自于{ { ; }之类的输入的模糊性,可以将其解析为包含不完整块的块或包含块的不完整块。

现在你可能会问:“为什么在没有额外的括号时会发生这种情况?解析器的单一令牌前瞻当然应该看到EOF并决定减少而不是移位?”事实上,如果您使用LR(1)解析器生成器,情况就是如此,但是bison(和yacc)使用LALR(1),它结合了前瞻性不同的状态。

此外,语法不接受您所说的内容 - 您的示例输入{ int i = 7; stdout(i); }将导致语法错误,因为缺少最终;(在}之后) 。您可以更改statement规则以摆脱;之后的block,在这种情况下,您将获得更多的转移/减少冲突,但它至少会接受输入你说你希望它接受。