Error: popping nterm

Error: popping nterm

我正在尝试理解 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):
    = nterm syntax (0.0-17: )
    = nterm top_levels (0.18-4.0: )
    = 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(, ); } ;

s : syntax top_levels END_OF_FILE { $$ = ; } ;

而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。问题

不要这样做:

<<EOF>>         { return END_OF_FILE; }

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

默认情况下,(f)lex 扫描仪 return 检测到 EOF 时为 0。这样一切就可以正常工作了。

当您尝试在 EOF 上发送不同的令牌时,您试图破坏此机制,但它不会起作用,因为开始符号不是接受规则。在减少起始符号后,解析器尝试减少 $accept 规则,因此它向扫描器询问另一个标记。但是扫描器已经命中了 EOF。在大多数情况下,扫描器将再次执行 <<EOF>> 操作(尽管不能保证),但这不会产生它需要的 $end 令牌。所以你得到一个语法错误。

2。潜在问题(可能)

通常,人们尝试这样做是为了创建一个用户操作,当输入被接受时 运行s,通常是为了 return 将解析结果 yyparse的调用者通过一个“out”参数。尝试在开始生产中明确识别 EOF 令牌(甚至 $end 令牌)是行不通的,但有一个更简单的解决方案:一个额外的单元规则:

%start return
%%
return: s  { *out = ; }
s: syntax top_levels  { $$ = ; }

请注意,您也可以在没有 top_levels 的情况下执行此操作:

%start return
%%
return: { *out = ; }
s: syntax { $$ = py_list(Py_None); }
  | s top_level { $$ = py_append(, ); } 

另一种方法是在启动规则的操作中使用特殊的 YYACCEPT 操作宏。但是,我相信上面概述的标准解决方案更简单,因为它不需要扫描仪提供任何东西。

3。跟踪输出

Error: popping nterm s (0.0-4.0: )

表示:

  1. 检测到语法错误。

  2. 作为错误恢复的一部分,解析器从堆栈中弹出 non-terminal s

  3. non-terminal 的源位置从 0.0 扩展到 4.0 (line . column)

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

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