You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:29:06