我使用lex& yacc编写VHDL解析器。 VHDL具有一些语言特性,使其以与C类似的方式对上下文敏感。例如,类似于类似于构造的构造会影响解析器是否应该将某些东西标记为IDENTIFIER与TYPEDEF_NAME。
当您需要根据&#34引用的另一个文件构建符号表时,会出现困难;使用"语句(类似于" import"在Java或Python中)。
library ieee;
use ieee.std_logic_1164.all;
-- code which uses something defined in ieee.std_logic_1164 package
在C中,这是相当简单的,因为预处理器已经将所有头文件组合成一个可以从上到下扫描的单个翻译单元。但是'使用' VHDL中的语句不是预处理程序命令。
所以,不知何故,当我解析文件时,我必须认识到当我看到一个use语句然后解析相关文件,然后继续使用该符号表解析原始文件。 / p>
使用lex / yacc有一种优雅的方法吗?我知道有yyrestart,但我不确定这是否会走上正轨。
答案 0 :(得分:1)
如果您使用flex
,则非常简单。
基本机制(包括两个正常运行的代码示例)在flex手册的"Multiple Input Buffers" chapter中有所描述。您还可以浏览this question on SO。
识别use
构造的解析器(yacc / bison)缩减可以包括调用yy_push_buffer
的代码。在示例代码中,扫描程序识别包含文件的末尾(lex / flex),它只是弹出缓冲区堆栈。
根据文件包含的正式规则,您可能希望解析器知道包含的文件已完成,以避免出现在包含文件中开始并继续在包含器中的语法结构。 (C允许这样,即使它几乎总是一个错误;我对VHDL一无所知,但肯定有一些语言不允许它。)一种可能性是递归调用解析器以解析包含文件,这将需要一个可重入("纯")解析器。在这种情况下,扫描程序在到达包含文件末尾时应该返回包含文件结尾的标记,因为包含的文件语法生成需要使用这样的标记终止。
您可能需要担心解析器已经请求下一个输入令牌的可能性。对于分号终止语句,大多数LALR(1)语法不依赖于先行标记,并且野牛通常不会在它不需要它的上下文中请求先行标记。但是所有与Posix兼容的yacc实现并不能保证这种行为,并且您可能正在使用不具备该行为的那种行为。
在这种情况下,您必须保留前瞻标记,以便在解析包含的文件后重新读取它。最方便的做法是将先行标记存放在扫描仪可以看到的地方,并让扫描仪在看到所包含文件的末尾时返回该标记(如果设置)。在野外行动中,您可以在yychar
中找到lookahead token,其语义值和位置(如果已启用位置)位于yylval
和yylloc
中。如果bison没有读取前瞻标记,则yychar
的值将为YYEMPTY
,并且当它即将推送输入缓冲区时,最简单的可能的bison实现将assert(yychar == YYEMPTY)
。如果断言失败,您将需要实施更复杂的策略。