如何在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
相关产品推荐
相关产品推荐

