在输入文件中到达 EOF 时如何做某事?

M-e*_*man 3 bison eof flex-lexer

我正在尝试跟踪所有函数的签名,以便检查 - 扫描所有输入文件时 - 是否已定义所有函数,否则报告错误。为此,我希望扫描器将END_OF_FILE令牌返回给解析器以调用我的检查过程,但是我收到了“令牌未定义”错误,尽管我在解析器中将其定义为令牌。

有什么建议吗?

ric*_*ici 5

发送您自己的文件结尾令牌很少是一个好主意,如果您选择这样做,则需要格外小心。

幸运的是,几乎从来没有必要这样做。

如果你想在解析结束之前执行代码,你可以在开始生产中这样做:

start: program { /* Code to execute at the end of the parse */ }
     ;
Run Code Online (Sandbox Code Playgroud)

如果您使用的是 bison,有一个警告:代码将在解析完成时执行,无论它是否成功完成。特别是,输入流中可能仍然存在未使用的令牌。[注1]

在许多情况下,这不是问题。错误将被立即检测到(除非操作调用YYACCEPT),即使解析失败,执行额外检查通常也不是问题。在某些应用程序中,您甚至可能需要这种行为;例如,如果您正在解析嵌入在较大文本中的表达式,并且不想坚持将解析的上下文扩展到文本的末尾。

但是,如果你真的需要知道解析是否完整与否,就足够了请检查的值yycharYYEOF(看到野牛手册了解详情。)所以,你可以将其替换先前的:

start: program { if (yychar == YYEOF) {
                    /* Code to execute at the end of the parse */
                 }
                 else {
                    /* There is definitely an error. Probably do nothing. */
                 }
               }
Run Code Online (Sandbox Code Playgroud)

如果你要发送你自己的文件尾令牌,你需要确保你仍然维护解析器和词法扫描器之间的契约,即:

  • 扫描器通过返回 0 作为标记值来指示输入结束;和
  • 解析器在收到 0 后不会请求另一个令牌。

虽然词法扫描器有时可以处理违反第二个条件的行为,但它是未定义的行为,在某些情况下,生成的扫描器会出现段错误或执行其他不需要的操作。并且由于解析器不会将您的自定义文件结束标记理解为指示输入结束,因此它会在收到后继续请求更多标记。

这意味着你真的需要同时发送你的令牌和正确的END令牌,这意味着做这样的事情:

%% 
   /* This code is inserted at the top of yylex */
   static int eof_reached = 0; /* Note: not reentrant */
   if (eof_reached) return END;
 /* ... */
<<EOF>> { eof_reached = 1; return MY_END_OF_FILE; }
Run Code Online (Sandbox Code Playgroud)

这会起作用,但由于无法重置 eof_reached 布尔值,因此扫描仪只能使用一次。您可以将其设为全局,或者您可以构建一个可重入扫描器并将其添加到扫描器上下文对象的额外数据部分。这些都是在调用 yylex 之间维护扫描器状态的有用技术,但在这种特殊情况下,我认为使用它们不会获得任何好处,因为如上所述,几乎不需要发送自定义输入端令牌。


至于您遇到的确切问题:

没有更多细节,无法回应:

我收到“令牌未定义”错误,

从何而来?野牛?柔性?编译器?消息具体说了什么?它指的是哪一行代码?(您是否准确地调用了文件结尾标记END_OF_FILE?)

笔记

  1. 由于原始 yacc 中处理输入结束标记的方式,原始 yacc 或 byacc 及其派生类不会发生这种情况。具有与原始 yacc 相同的输入结束处理的解析器生成器通常不会执行与开始产生式关联的操作,除非已经遇到输入结束标记。