我在野牛中编写了一个解析器(在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
我做错了什么?
答案 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
,在这种情况下,您将获得更多的转移/减少冲突,但它至少会接受输入你说你希望它接受。