我正在使用Verilog解析器,使用Bison从语言的正式规则中创建解析器。
BNF中的这种正式语法规范来自IEEE标准1364-2001“IEEE标准Verilog硬件描述语言参考手册(LRM)。
variable_lvalue ::=
hierarchical_variable_identifier
| hierarchical_variable_identifier [ expression ] { [ expression ] }
| hierarchical_variable_identifier [ expression ] { [ expression ] } [ range_expression ]
| hierarchical_variable_identifier [ range_expression ]
| variable_concatenation
省略分层标识符,为了清晰起见,将单个表达式减少为range_expression
到其自己的规则中并使用左递归(range_expression
可能会或可能不会在列表的末尾)这就是我所拥有的。
nonempty_expression_in_brackets_list
: OPENBRACKETS range_expression CLOSEBRACKETS { }
| OPENBRACKETS expression CLOSEBRACKETS { }
| OPENBRACKETS expression CLOSEBRACKETS nonempty_expression_in_brackets_list { }
;
range_expression
的定义如下。
range_expression ::=
expression
| msb_constant_expression: lsb_constant_expression
| base_expression +: width_constant_expression
| base_expression -: width_constant_expression
省略将单个表达式减少到range_expression的规则,这就是我所拥有的。
range_expression
: constant_expression COLON constant_expression { }
| expression PLUS COLON constant_expression { }
| expression MINUS COLON constant_expression { }
;
即使在最简单的情况下,只使用一个表达式,功能也会出现问题。具体来说,解析器无法决定是否将括号后的标识符缩减为constant_function_argument
或function_argument
(我希望constant_function_call
或function_call
位于规则,但不是两个)。例如,在解析的文本中这样的东西会有问题。
IDENTIFIER [ IDENTIFIER ( IDENTIFIER ) ]
第三个标识符是否缩减为function_argument
,预计稍后会减少到function_call
,最后会减少到expression
,或者减少到constant_function_argument
,预计会减少到constant_function_call
constant_expression
,最后是object = {
id: 1,
Oid: 'ff2d344e-77e8-43b3-a6f3-bfba577d6abd',
name: 'Some name'
}
?
我认为两者在这一点上都是正确的,在我们看到CLOSEBRACKETS或COLON或PLUS COLON或MINUS COLON之前我们无法确定。
我是否应该添加bison的glr选项来克隆解析器并使其在冲突时消失?是否有不同的方式来编写语法?
答案 0 :(得分:1)
我对Verilog并不是很了解,OP中甚至没有文档链接;此外,大多数参考语法产品也不在OP中。所以我能做的最好的就是提供一些一般的指导,结合我对Verilog的一些知识片段 - 正如我所理解的那样,这是非正常的解析。
如上所述,在解析variable_lvalue
时存在(至少)两个问题,尽管最终它们出乎意料地相似:
Verilog语法似乎需要在常量和非常量表达式之间进行语法区分;以及
[...]
中hierarchical_variable_name
有(至少)三种不同的含义,它们的语法略有不同但重叠。 (我在这里被一个不同的SO问题引导;这个问题只显示三种语法中的两种。)
从技术上讲,语法提供constant_expression
和非限定expression
,因为每个表达式上下文都是从expression
或constant_expression
生成的(或者是一个同义词这两个非终端)。但是,在自下而上的解析器中,如果两个表达式语法是互斥的,则会更容易。忽略运算符优先级,基本方案将是:
expression : constant_expression
| non_constant_expression;
non_constant_expression: non_constant_primary
| unary_operator non_constant_primary
| non_constant_expression binary_operator expression
| constant_expression binary_operator non_constant_expression
constant_expression : constant_primary
| unary_operator constant_primary
| constant_expression binary_operator constant_expression
除了区分constant_primary
和non_constant_primary
的问题外,这种方式也可以。显然,文字(数字和字符串)是constant_primary
,并且(实际上)具有至少两个组件的层次名称是non_constant_primary
。但简单的标识符可能是。
对于必须在使用前声明标识符的那些情况(或语言),可以通过在符号表中查找标识符来区分常量和非常量标识符。
为了使其工作,需要与词法分析器共享符号表,并且词法分析器需要有一定数量的关于名称查找的逻辑,特别是如果语言允许使用范围名称。许多C语法分析器正是这样做的,因为有些表达式在不知道标识符是类型还是变量的情况下无法解析。 ((something)+1/2
,例如:如果something
为double
,则为0.5;如果something
为值为0.0
的变量,则为0.0。
但是这个策略因常量函数而崩溃,因为在使用之前不需要声明常量函数。 (允许相互递归的常量函数。)尽管如此,因为常量函数调用是在与非常量函数调用不同的阶段中进行评估的,所以决定每个调用站点是否是一个常量调用是很重要的。
此外,在解析器和词法分析器之间共享符号表违反了关注点的分离。它并不像听起来那么容易:词法分析器无法知道标识符的实例是定义还是直接使用。对于词法分析者来说,区分标识符令牌作为非限定名称或作为分层名称的一部分是非常重要的。 (我不知道分层子组件名称是否可以与参数名称相同,但它们可能是合理的;当然,在大多数语言中,结构成员名称可以拼写为与全局常量完全相同而不会产生任何歧义。)
除了明确需要进行语义分析以确定给定的函数标识符是否有资格成为constant_function_identifier之外,在我看来,唯一合理的策略是生成AST,解析表达式而不管它们是否有效应该是常量与否,然后对AST执行验证遍历,以验证在需要表达式为常量的那些上下文中只发生常量表达式。 (可能会有一个中间AST来确定哪些函数可以用作常量函数调用。)
这将简化语法,并将常量表达式分析限制在一个明确定义的界面上,而不是将其涂抹在整个语法上。
第二个问题是使用括号的结果:
作为生成名称的选择器(在这种情况下只允许使用文字整数)
作为数组索引(在这种情况下允许任意表达式)
作为范围定义,可能看起来像数组索引,但也可能是由冒号分隔的两个常量表达式(但不一定是文字)。
单个选择器可以跟随分层名称中的任何组件,而数组索引和范围表达式是遵循分层名称的后缀运算符;如果存在,范围表达必须是最后的。
据我了解,选择器和数组索引是必需的或禁止的,具体取决于命名对象的声明。但是,如果不知道标识符的声明,这三种用法并不总是可以区分的:[1]
可以是三种可能中的任何一种。
理论上,在符号表中查找标识符可以解决问题。但是有许多可能出现标识符的背景;期望词法分析器确定何时坚持使用选择器或索引可能会让词法分析器复制过多的解析器工作。
再一次,似乎在语义规则中进行分析要容易得多。在这种情况下,我相信,可以在分析时进行分析,因为需要预先声明,因此进行检查的适当位置将在variable_lvalue
的缩减规则中。
如果你走这条路,那么语法就没那么复杂了。你可以使用一种语法来接受一些语法上不正确的输入,知道所有这些错误都会被语义规则检测到。最简单的语法只接受.name
和[...]
后缀的任何序列:(见注1)
hierarchical_lvalue: hierarchical_component
| hierarchical_lvalue '.' hierarchical_component
| hierarchical_lvalue '[' expression ']'
| hierarchical_lvalue '[' expression ':' expression ']'
| hierarchical_lvalue '[' expression "+:" expression ']'
| hierarchical_lvalue '[' expression "-:" expression ']'
在这个答案中,我避免使用像:这样的单字符标记的笨拙的符号名称。 Bison允许您将其写为':'
(在flex动作中,您return ':';
),甚至不必将其声明为%token
;结果是IMO更易读,更容易维护。我还使用了双引号令牌别名来提高可读性;而不是写作
%令牌PLUS_COLON %% ....:表达式PLUS_COLON表达式......
您可以声明别名
%token PLUS_COLON "+:"
%%
.... : expression "+:" expression ...
在这种情况下,词法分析器仍然需要使用符号名称,但语法更容易阅读。 (如果您使用扩展错误消息,则错误消息也是如此:bison将能够告诉用户":", "+:" or "-:"
是预期的。)