如何编写处理导入语句的解析器?

How to write parser which handles import statements?

我正在使用 lex 和 yacc 编写 VHDL 解析器。 VHDL 具有一些语言特性,使其以类似于 C 的方式对上下文敏感。例如,类似 typedef 的结构会影响解析器是否应将某些内容标记为 IDENTIFIER 与 TYPEDEF_NAME.

当您需要根据 "use" 语句引用的另一个文件(类似于 Java 中的 "import" 或Python).

library ieee;
use ieee.std_logic_1164.all;

-- code which uses something defined in ieee.std_logic_1164 package

在 C 中,这是相当简单的,因为预处理器已经将所有头文件组合成一个可以从上到下扫描的翻译单元。但是 'use' VHDL 中的语句不是预处理器命令。

所以,不知何故,当我解析文件时,我必须识别何时看到 use 语句,然后去解析相关文件,然后继续解析带有该符号的原始文件 table.

lex/yacc 有什么优雅的方法吗?我知道有 yyrestart 但我不确定它是否在正确的轨道上。

如果你用的是flex,那就很简单了。

"Multiple Input Buffers" chapter of the flex manual. You can also take a glance at .

中描述了基本机制(包括两个功能代码示例)

识别use构造的解析器(yacc/bison)缩减可以包含调用yy_push_buffer的代码。在示例代码中,扫描器 (lex/flex) 识别包含文件的末尾,它只是弹出缓冲区堆栈。

根据文件包含的正式规则,您可能希望解析器知道包含文件已完成,以避免语法结构从包含文件开始并在包含程序中继续。 (C 允许这样做,即使它几乎总是一个错误;我对 VHDL 一无所知,但肯定有一些语言不允许这样做。)一种可能性是递归调用解析器以解析包含的文件,这将需要一个可重入 ("pure") 解析器。在这种情况下,扫描器在到达包含文件的末尾时应该 return 包含文件结束标记,因为您的包含文件语法生成需要使用这样的标记终止。

您可能需要担心解析器可能已经请求了下一个输入令牌。大多数 LALR(1) 语法不依赖于以分号终止的语句的先行标记,并且 bison 通常不会在不需要它的上下文中请求先行标记。但是所有 Posix 兼容的 yacc 实现都不能保证这种行为,您可能正在使用一个不兼容的实现。

在这种情况下,您必须保留先行标记,以便在解析包含的文件后可以重新读取它。最方便的做法是将先行标记存储在扫描器可以看到的地方,并在扫描器看到包含文件的末尾时让扫描器 return 该标记(如果已设置)。在 bison 操作中,您可以在 yychar 中找到 lookahead token,其语义值和位置(如果启用了位置)在 yylvalyylloc 中。如果 bison 没有读取 lookahead 标记,yychar 的值将是 YYEMPTY,并且最简单的 bison 实现将在它即将推送输入缓冲区时 assert(yychar == YYEMPTY)。如果断言失败,您将需要实施更复杂的策略。