错误:弹出nterm

时间:2018-04-08 12:43:01

标签: parsing bison flex-lexer

我正在尝试理解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的问题究竟是什么?对我来说,似乎它实际上接受了整个事情,然后决定拒绝它,因为它第二次红色令牌......但整个报告非常令人困惑。

1 个答案:

答案 0 :(得分:2)

1。问题

不要这样做:

<<EOF>>         { return END_OF_FILE; }

Yacc / bison解析器使用内部规则扩充语法,该内部规则生成起始符号,后跟一个名为$end的内部eof标记,其标记号为0.(您可以在状态3和6中看到此规则)这是语法中唯一接受的规则。

默认情况下,(f)检测到EOF时,lex扫描仪返回0。所以Just Works。

当您尝试在EOF上发送不同的令牌时,您试图打败此机制,但它不起作用,因为起始符号不是接受规则。在开始减少symbil之后,解析器会尝试减少$accept规则,因此它会要求扫描程序提供另一个令牌。但是扫描仪已经打了EOF。在大多数情况下,扫描程序将再次执行<<EOF>>操作(虽然无法保证),但这不会产生所需的$end令牌。所以你得到一个语法错误。

2。潜在的问题(可能)

通常,人们尝试这样做是为了创建一个在接受输入时运行的用户操作,通常是为了通过“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操作宏。但是,我相信上面列出的标准解决方案更简单,因为扫描仪不需要任何东西。

3。跟踪输出

Error: popping nterm s (0.0-4.0: )

意思是:

  1. 检测到语法错误。

  2. 作为错误恢复的一部分,解析器从堆栈中弹出非终端s

  3. 非终端的源位置从0.0扩展到4.0(line . column

  4. 如果s(或其语义类型)有一个已注册的析构函数,那么它将在步骤2中运行。您可能希望为引用Python值的语法类型注册析构函数,以减少其引用计数这样你就不会在语法错误上泄漏内存。但也许我错了。

    此外,您可以为句法值注册一个%printer,在这种情况下,它将在冒号后打印。