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

如何在JavaCC中为搜索表达式语法设置所有运算符的左结合优先级

问题根源

你当前的语法规则默认右结合,是因为规则结构为基础节点 (运算符 完整表达式)*,每次匹配运算符后,右侧会优先解析为完整的高优先级表达式,自然形成右优先的结合逻辑。你之前尝试的左递归写法JavaCC不支持,而迭代写法如果没有分层处理优先级和节点累积,要么报错要么结合顺序不符合要求。

解决方案

采用分层优先级+迭代左累积的写法,既可以解决左递归问题,也能实现左结合,同时还能控制不同运算符的优先级(比如默认搜索表达式优先级为 NOT > AND > OR,符合常规使用习惯)。

代码修改示例

保留你现有的SKIP、TOKEN、Term、KeyPhrase、Field规则不变,修改表达式相关的规则如下:

ASTStart Start(): {} {
  OrExpression() <EOF>
  { return jjtThis; }
}

// 处理OR运算,左结合
ASTOrExpression OrExpression(): {
  Node left;
  Node right;
  Token op;
} {
  left = AndExpression()
  (
    op=<OR> right=AndExpression()
    {
      // 新建OR节点,把之前的左半部分作为左子节点,新匹配的表达式作为右子节点
      ASTOrExpression orNode = new ASTOrExpression(ParserTreeConstants.JJTOREXPRESSION);
      orNode.jjtAddChild(left, 0);
      orNode.jjtAddChild(right, 1);
      orNode.jjtSetValue(op.image);
      // 更新左节点为当前新建的OR节点,供下一次迭代累积
      left = orNode;
    }
  )*
  { return left; }
}

// 处理AND运算,左结合,优先级高于OR
ASTAndExpression AndExpression(): {
  Node left;
  Node right;
  Token op;
} {
  left = NotExpression()
  (
    op=<AND> right=NotExpression()
    {
      ASTAndExpression andNode = new ASTAndExpression(ParserTreeConstants.JJTANDEXPRESSION);
      andNode.jjtAddChild(left, 0);
      andNode.jjtAddChild(right, 1);
      andNode.jjtSetValue(op.image);
      left = andNode;
    }
  )*
  { return left; }
}

// 处理NOT运算,一元运算符默认右结合即可
ASTNotExpression NotExpression(): {
  Token op;
  Node operand;
} {
  op=<NOT> operand = Operand()
  {
    ASTNotExpression notNode = new ASTNotExpression(ParserTreeConstants.JJTNOTEXPRESSION);
    notNode.jjtAddChild(operand, 0);
    notNode.jjtSetValue(op.image);
    return notNode;
  }
  | operand = Operand()
  { return operand; }
}

// 最小操作单元:括号表达式或者Term
Node Operand(): {
  Node expr;
} {
  <LROUND> expr=OrExpression() <RROUND> { return expr; }
  | expr=Term() { return expr; }
}

上述写法默认LOOKAHEAD=2即可正常运行,不会出现歧义,对于keyphrase[field] AND keyphrase[field] OR keyphrase[field]的表达式,会自动生成(A AND B) OR C的左结合结构。

原错误迭代写法的问题说明

你之前尝试的(Operand() Operator())* Operand()写法报错,是因为JavaCC无法判断循环终止的时机,需要额外配置LOOKAHEAD规则,且没有分层处理优先级,即使修改LOOKAHEAD跑通,也无法区分AND和OR的优先级,会导致同级运算符全部左结合,不符合常规搜索表达式的优先级逻辑。

内容的提问来源于stack exchange,提问作者Kronos24

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 05:15:04