我正在尝试理解Flex提供的诊断消息:
Entering state 5
Return for a new token:
Reading a token: Next token is token END_OF_FILE (4.0: )
Shifting token END_OF_FILE (4.0: )
Entering state 43
Reducing stack by rule 143 (line 331):
$1 = nterm syntax (0.0-17: )
$2 = nterm top_levels (0.18-4.0: )
$3 = token END_OF_FILE (4.0: )
-> $$ = nterm s (0.0-4.0: )
Stack now 0
Entering state 3
Return for a new token:
Reading a token: Next token is token END_OF_FILE (4.0: )
4/0: syntax error
Error: popping nterm s (0.0-4.0: )
Stack now 0
Cleanup: discarding lookahead token END_OF_FILE (4.0: )
Stack now 0
我无法理解为什么/它试图用EOF令牌做什么。以下是Flex规则:
<<EOF>> { return END_OF_FILE; }
野牛规则:
top_level : message
| enum
| service
| import { $$ = Py_None; }
| package { $$ = Py_None; }
| option_def { $$ = Py_None; }
| ';' { $$ = Py_None; } ;
top_levels : %empty { $$ = py_list(Py_None); }
| top_levels top_level { $$ = py_append($1, $2); } ;
s : syntax top_levels END_OF_FILE { $$ = $2; } ;
Bison生成的输出文件:
State 3
0 $accept: s . $end
$end shift, and go to state 6
State 5
142 top_levels: top_levels . top_level
143 s: syntax top_levels . END_OF_FILE
BOOL shift, and go to state 9
... bunch of similar rules
END_OF_FILE shift, and go to state 43
';' shift, and go to state 44
import go to state 45
... bunch of similar rules
top_level go to state 55
State 6
0 $accept: s $end .
$default accept
我不知道发生了什么。为什么报告两次阅读EOF令牌?弹出s
的问题究竟是什么?对我来说,似乎它实际上接受了整个事情,然后决定拒绝它,因为它第二次红色令牌......但整个报告非常令人困惑。
答案 0 :(得分:2)
不要这样做:
<<EOF>> { return END_OF_FILE; }
Yacc / bison解析器使用内部规则扩充语法,该内部规则生成起始符号,后跟一个名为$end
的内部eof标记,其标记号为0.(您可以在状态3和6中看到此规则)这是语法中唯一接受的规则。
默认情况下,(f)检测到EOF时,lex扫描仪返回0。所以Just Works。
当您尝试在EOF上发送不同的令牌时,您试图打败此机制,但它不起作用,因为起始符号不是接受规则。在开始减少symbil之后,解析器会尝试减少$accept
规则,因此它会要求扫描程序提供另一个令牌。但是扫描仪已经打了EOF。在大多数情况下,扫描程序将再次执行<<EOF>>
操作(虽然无法保证),但这不会产生所需的$end
令牌。所以你得到一个语法错误。
通常,人们尝试这样做是为了创建一个在接受输入时运行的用户操作,通常是为了通过“out”参数将解析结果返回给yyparse
的调用者。尝试在开始生产中明确识别EOF令牌(甚至是$end
令牌)是行不通的,但有一个更简单的解决方案:额外的单位规则:
%start return
%%
return: s { *out = $1; }
s: syntax top_levels { $$ = $2; }
请注意,您也可以在没有top_levels
的情况下执行此操作:
%start return
%%
return: { *out = $1; }
s: syntax { $$ = py_list(Py_None); }
| s top_level { $$ = py_append($1, $2); }
另一种方法是在开始规则的操作中使用特殊的YYACCEPT
操作宏。但是,我相信上面列出的标准解决方案更简单,因为扫描仪不需要任何东西。
Error: popping nterm s (0.0-4.0: )
意思是:
检测到语法错误。
作为错误恢复的一部分,解析器从堆栈中弹出非终端s
。
非终端的源位置从0.0扩展到4.0(line . column
)
如果s
(或其语义类型)有一个已注册的析构函数,那么它将在步骤2中运行。您可能希望为引用Python值的语法类型注册析构函数,以减少其引用计数这样你就不会在语法错误上泄漏内存。但也许我错了。
此外,您可以为句法值注册一个%printer
,在这种情况下,它将在冒号后打印。