ANTLR表达式中允许逗号等特殊字符的实现方案咨询
问题描述
我用ANTLR实现基于Map的动态表达式解析,参考现有方案完成基础功能后,新增了IN、STARTSWITH、ENDSWITH等条件。在实现IN功能时,计划用逗号分割字符串集合,但传入表达式时触发错误:
line 1:64 token recognition error at: ','
需要解决如何让表达式支持逗号等特殊字符,同时正确解析IN操作的集合参数。
当前使用的语法文件、EvalVisitor修改代码及测试表达式如下:
现有语法文件
grammar SimpleBoolean; parse : expression EOF ; expression : LPAREN expression RPAREN #parenExpression | NOT expression #notExpression | left=expression op=comparator right=expression #comparatorExpression | left=expression op=binary right=expression #binaryExpression | bool #boolExpression | IDENTIFIER #identifierExpression | DECIMAL #decimalExpression ; comparator : GT | GE | LT | LE | EQ | NE | IN | NOTIN | STARTSWITH | ENDSWITH | NULL | NOTNULL ; binary : AND | OR ; bool : TRUE | FALSE ; AND : 'AND' ; OR : 'OR' ; NOT : 'NOT'; TRUE : 'TRUE' ; FALSE : 'FALSE' ; GT : '>' ; GE : '>=' ; LT : '<' ; LE : '<=' ; EQ : '=' ; NE : '!=' ; IN : 'IN' ; NOTIN : 'NOTIN' ; STARTSWITH : 'STARTSWITH' ; ENDSWITH : 'ENDSWITH' ; NULL : 'NULL' ; NOTNULL : 'NOTNULL' ; LPAREN : '(' ; RPAREN : ')' ; DECIMAL : '-'? [0-9]+ ( '.' [0-9]+ )? ; IDENTIFIER : [a-zA-Z_] [a-zA-Z_0-9]* ; WS : [ \r\t\u000C\n]+ -> skip;
修改后的EvalVisitor代码片段
if (ctx.op.EQ() != null) { return this.visit(ctx.left).equals(this.visit(ctx.right)); } else if (ctx.op.IN() != null) { String checkVal[] = this.visit(ctx.right).toString().split(","); boolean valuePresent = false; for(String value : checkVal) { if(value.equals(this.visit(ctx.left).toString())) valuePresent = true; } return valuePresent; }
测试表达式
ID = ID AND NOT ( comments = comments AND system IN system,admin,developer )
解决方案
1. 错误原因
当前语法未定义逗号作为合法Token,也未为IN/NOTIN操作设计专门的集合参数规则——IN操作的右侧应该是括号包裹的逗号分隔元素列表,而非普通表达式,原语法的comparatorExpression规则无法适配这种结构,导致解析器识别逗号时失败。
2. 修改语法文件
需要新增逗号Token,并为IN/NOTIN单独定义语法规则,调整expression的结构:
grammar SimpleBoolean; parse : expression EOF ; expression : LPAREN expression RPAREN #parenExpression | NOT expression #notExpression | left=expression op=inComparator right=inList #inExpression | left=expression op=comparator right=expression #comparatorExpression | left=expression op=binary right=expression #binaryExpression | bool #boolExpression | IDENTIFIER #identifierExpression | DECIMAL #decimalExpression ; // 拆分IN/NOTIN到单独的比较器规则 inComparator : IN | NOTIN ; // 普通比较器规则 comparator : GT | GE | LT | LE | EQ | NE | STARTSWITH | ENDSWITH | NULL | NOTNULL ; // 定义IN操作的集合列表 inList : LPAREN (expression (',' expression)*)? RPAREN ; binary : AND | OR ; bool : TRUE | FALSE ; AND : 'AND' ; OR : 'OR' ; NOT : 'NOT'; TRUE : 'TRUE' ; FALSE : 'FALSE' ; GT : '>' ; GE : '>=' ; LT : '<' ; LE : '<=' ; EQ : '=' ; NE : '!=' ; IN : 'IN' ; NOTIN : 'NOTIN' ; STARTSWITH : 'STARTSWITH' ; ENDSWITH : 'ENDSWITH' ; NULL : 'NULL' ; NOTNULL : 'NOTNULL' ; LPAREN : '(' ; RPAREN : ')' ; COMMA : ',' ; // 新增逗号Token DECIMAL : '-'? [0-9]+ ( '.' [0-9]+ )? ; IDENTIFIER : [a-zA-Z_] [a-zA-Z_0-9]* ; WS : [ \r\t\u000C\n]+ -> skip;
3. 调整EvalVisitor逻辑
不再通过字符串分割处理集合,直接遍历解析后的inList元素:
// 新增inExpression的处理逻辑 @Override public Boolean visitInExpression(SimpleBooleanParser.InExpressionContext ctx) { Object leftValue = visit(ctx.left); List<SimpleBooleanParser.ExpressionContext> elements = ctx.inList().expression(); boolean isPresent = false; for (SimpleBooleanParser.ExpressionContext elemCtx : elements) { Object elemValue = visit(elemCtx); if (leftValue.equals(elemValue)) { isPresent = true; break; } } // 处理NOTIN的反向逻辑 if (ctx.op.NOTIN() != null) { return !isPresent; } return isPresent; }
4. 修正测试表达式
IN操作的集合必须用括号包裹,修正后的表达式:
ID = ID AND NOT ( comments = comments AND system IN (system,admin,developer) )
内容的提问来源于stack exchange,提问作者User1512
相关产品推荐
相关产品推荐

