如何在YACC中区分比较运算符<、>与泛型<>
这绝对是写带泛型的表达式文法时最头疼的移进/归约冲突问题——YACC默认没法区分<到底是比较运算符,还是泛型参数列表的起始符号。结合你给出的示例和冲突详情,咱们来一步步解决这个问题:
问题背景
先明确咱们要处理的目标代码和当前遇到的冲突细节:
目标示例代码
a < getBoolNum<int>(a < getNum<int>())
当前YACC文法代码
translation_unit : expression {} ; expression : additive_expression GT additive_expression {} | additive_expression LT additive_expression {} | expression GT additive_expression {} | expression LT additive_expression {} ; additive_expression : function_call {} | IDENTIFIER {} ; function_call : IDENTIFIER LT IDENTIFIER GT LP argument_list RP // identifier < identifier > ( expression, expression ) {} ; argument_list : IDENTIFIER {} | function_call {} | expression {} | argument_list COMMA IDENTIFIER {} | argument_list COMMA function_call {} | argument_list COMMA expression {} ;
冲突情况汇总
- 总计3处移进/归约冲突
- 状态1冲突:1处移进/归约
- 状态19冲突:1处移进/归约
- 状态25冲突:1处移进/归约
状态1冲突详情
7 additive_expression: IDENTIFIER .
8 function_call: IDENTIFIER . LT IDENTIFIER GT LP argument_list RP
LT 移进,进入状态6
LT [使用规则7归约(additive_expression)]
$default 使用规则7归约(additive_expression)
解决思路与具体方案
核心逻辑是给YACC明确的上下文判断依据:当<紧跟在标识符(可能是泛型函数名)后面,且后续能匹配IDENTIFIER>结构时,它是泛型参数起始;否则是比较运算符。具体可以通过以下步骤实现:
1. 调整文法的优先级声明
先给泛型的<设置比比较运算符更高的优先级,避免YACC过早把标识符归约为additive_expression。在YACC文件开头添加:
%nonassoc LT GT // 比较运算符的优先级(较低) %nonassoc LT_GT_START // 泛型<的优先级标记(更高)
2. 改写文法结构,区分上下文场景
关键是重构表达式规则,让YACC优先尝试匹配泛型函数调用的结构,而不是先处理比较运算。修改后的文法如下:
%nonassoc LT GT %nonassoc LT_GT_START translation_unit : expression {} ; // 顶层表达式 expression : additive_expression {} ; // 后缀表达式:处理泛型调用和比较运算 additive_expression : postfix_expression {} ; postfix_expression : IDENTIFIER {} // 泛型函数调用:优先匹配这个分支 | postfix_expression LT IDENTIFIER GT LP argument_list RP %prec LT_GT_START {} // 比较运算:< 和 > | postfix_expression LT additive_expression {} | postfix_expression GT additive_expression {} ; // 参数列表简化为表达式列表即可 argument_list : expression {} | argument_list COMMA expression {} ;
3. 为什么这样改能解决冲突?
- 当标识符后紧跟
<时,YACC会优先尝试匹配泛型函数调用的规则(因为我们给这个分支加了更高优先级的LT_GT_START标记),不会过早把标识符归约为additive_expression。 - 对于普通的比较运算(比如
a < getBoolNum<...>),因为<后面没有匹配IDENTIFIER>的结构,YACC会 fallback 到比较运算符的规则,正确识别为比较逻辑。
额外验证提示
你可以用修改后的文法测试示例代码:
getBoolNum<int>会被正确识别为泛型函数名+参数,不会被误判为getBoolNum < int的比较。a < getBoolNum<...>则会正确解析为比较表达式。
内容的提问来源于stack exchange,提问作者Javin Yang
相关产品推荐
相关产品推荐

