ANTLR4逻辑表达式解析未拦截非法语法的问题排查求助
问题根因
你的语法、词法设计存在4个核心疏漏,直接导致非法查询漏判:
- 空白处理逻辑混乱:你在AND、OR、括号、比较符等几乎所有token的匹配规则前后硬编码了
' '*来匹配空格,同时又重复定义了SPACE、WS两个功能完全一致的、将空白送入隐藏通道的规则。这种写法完全破坏了ANTLR统一的空白处理逻辑,还会干扰词法的最长匹配规则,导致单字符&、|的识别异常。 - 入口规则未强制全串匹配:你的入口规则
query: (expression)+;没有加EOF终止符,ANTLR默认只要输入串的任意前缀能匹配规则就算解析成功,不需要处理完所有输入字符。这就导致解析器碰到第一个合法表达式后,不管后面跟的是单个&、单个|还是其他非法内容,都会直接返回前面的匹配结果,剩余的非法输入被完全忽略,不会触发错误。 - 语法规则允许无运算符连接表达式:你在
query规则和expressionGroup规则里都用了(expression)+的写法,允许多个表达式连续出现、中间不需要任何逻辑运算符,这直接导致两个表达式直接靠空格拼接、甚至完全挨在一起的非法写法,会被解析成多个顺序排列的合法expression,不会触发语法错误。 - 语法规则存在冗余二义性:你单独写了
expressionGroup logicalOperator expressionGroup分支,但expressionGroup本身已经是expression的可选分支,重复定义会产生解析二义性,干扰正常的逻辑表达式匹配。
修改方案
按下面的步骤调整,即可正确拦截所有非法查询:
- 统一空白处理逻辑
删掉所有token规则前后硬编码的' '*,只保留一个送入隐藏通道的空白规则,所有关键字、运算符仅匹配符号本身。注意你贴的代码里出现的&、"是HTML转义结果,实际编写g4文件时要替换为原生的&、"字符。改完的词法规则部分如下:// 唯一的空白处理规则,统一送入隐藏通道 WS: [ \t\r\n]+ -> channel(HIDDEN); // 关键字、运算符仅匹配核心符号,不带前后空格 AND: '&&' | 'AND' | 'and'; OR: '||' | 'OR' | 'or'; NOT: '!' | 'not' | 'NOT'; HAS: 'HAS' | 'has'; IN: 'IN' | 'in'; EQ: '='; NEQ: '!='; L_SQ_BRACKET: '['; R_SQ_BRACKET: ']'; L_RND_BRACKET: '('; R_RND_BRACKET: ')'; COMMA: ','; KEY_PREFIX: 'tags('; IDENTIFIER: [A-Za-z0-9_]+ ; ESCAPED_IDENTIFIER: '"' ( '""' | [^"] )* '"'; STRING_LITERAL: '\'' [A-Za-z0-9_ ]* '\''; // 放在所有词法规则最后,兜底匹配所有非法字符,触发词法错误 INVALID_CHAR: . ; - 修正语法规则,强制全串匹配、禁止无运算符连写表达式
删掉冗余的表达式组逻辑运算分支,在入口规则末尾加EOF强制匹配完所有输入,移除所有允许多个表达式连续出现的+量词,明确多表达式之间必须存在逻辑运算符。改完的语法规则部分如下:query : expression EOF // 强制匹配完整输入,避免提前截断忽略非法内容 ; expression : keyValue #comparisonExpression | inExpression #singleInExpression | hasExpression #singleHasExpression | expressionGroup #singleExpressionGroup | NOT expression #notExpression | expression logicalOperator expression #logicalExpression ; inExpression : key IN literalList ; hasExpression : HAS key ; expressionGroup : L_RND_BRACKET expression R_RND_BRACKET ; keyValue : key comparisonOperator literal ; key : KEY_PREFIX keyName R_RND_BRACKET ; keyName : IDENTIFIER ; comparisonOperator : EQ | NEQ ; logicalOperator : AND | OR ; literalList : L_SQ_BRACKET literal (COMMA literal)* R_SQ_BRACKET ; literal : STRING_LITERAL ;
验证效果
调整后所有非法输入都会被正确拦截:
- 含单个&、单个|的输入会被
INVALID_CHAR规则捕获,触发词法错误 - 两个表达式中间仅靠空格拼接、或完全无间隔连写的输入,会因为缺少逻辑运算符触发语法错误
- 合法的带
&&/||连接符的查询可正常解析,不受影响。
内容的提问来源于stack exchange,提问作者Aditya Jagtiani
相关产品推荐
相关产品推荐

