Antlr4自定义SpringEL风格表达式语法:如何让Parser检测出非法数字(如3ww)的语法错误?
嘿,这个问题我之前帮人排查过类似的!核心原因是你的解析器只匹配了输入的前缀部分,没有验证整个输入是否完全符合语法,再加上原语法规则的结构让解析器错误地把3ww拆成了合法的Integer(3)和Identifier(ww),完全没意识到这是个非法的整体。
问题根源
你碰到的场景:
- 合法输入
#user.age>3能正常解析 - 非法输入
#user.age>3ww或#user.age>3ww && #user.age<2却没报错
这是因为原语法的statement规则只要求匹配一个expr,但没有强制匹配到输入的末尾(EOF),所以解析器匹配完#user.age>3就停止了,直接忽略后面的ww,自然不会触发错误。
修改后的完整语法规则
下面是调整后的代码,关键修改点我会逐一说明:
grammar FlowExpr; // 核心修改:强制匹配整个输入到末尾,非法后缀会直接触发错误 statement : expr EOF; // 重构表达式规则,明确优先级(从高到低),避免解析器乱组合 expr : Not expr # NotExpr | expr (Mul|Div) expr # MulDivExpr | expr (Add|Sub) expr # AddSubExpr | expr BooleanOperator expr # CompareExpr | expr And expr # AndExpr | expr Or expr # OrExpr | atom # AtomExpr; // 定义所有最小的原子表达式单元,明确边界 atom : boolean | invoker | entity | number | String | '(' expr ')'; // 补充括号支持,符合常规表达式逻辑 entity : ObjectSymbol Identifier; boolean : 'true'|'false'; invoker : entity attribute+; attribute : Dot Identifier; number : Integer | Float ; // 词法规则 Identifier : Letter (Letter|JavaIDDigit)* ; Integer : '-'? INT ; Float : '-'? INT Dot [0-9]+; // 确保浮点数至少有一个小数位(可按需改回[0-9]*) Mul : '*'; Div : '/'; Add : '+'; Sub : '-'; BooleanOperator : '>'|'>='|'<'|'<='|'=='|'!='; And : '&&'; Or : '||'; Not:'!'; Dot : '.'; ObjectSymbol : '#'; String : '"' (~["\\] | '\\' .)* '"'; // 补全字符串匹配规则 // 辅助片段定义 fragment INT : '0' | [1-9][0-9]*; fragment Letter : [a-zA-Z_]; fragment JavaIDDigit : [0-9a-zA-Z_]; WS : [ \t\n\r]+ -> skip ;
关键修改点解析
给
statement加上EOF
这是最关键的一步!原规则statement : expr;只要求解析器找到一个合法表达式就结束,不管后面有没有垃圾字符。加上EOF后,解析器必须匹配整个输入的所有字符,比如3ww会被检测到3之后的ww无法匹配任何合法规则,直接报错。重构
expr规则,明确原子边界
把所有最小单元(数字、布尔值、实体等)放到atom规则里,让表达式结构更清晰,避免解析器把数字和后面的标识符错误组合成“合法”表达式。同时调整了优先级,让语法逻辑符合常规运算顺序(逻辑非>乘除>加减>比较>逻辑与或)。补全
String词法规则
原代码里String规则是空的,这里补充了标准的双引号字符串匹配逻辑,支持转义字符。优化
Float规则(可选)
原规则允许像-.这种非法浮点数,修改为[0-9]+确保至少有一个小数位;如果需要支持.5这种格式,可以改成'-'? (INT Dot [0-9]* | Dot [0-9]+)。
测试效果
修改后:
- 合法输入
#user.age>3或#user.age>3 && #user.age<2能正常解析 - 非法输入
#user.age>3ww或#user.age>3ww && #user.age<2会直接触发语法错误,完全符合你的预期。
内容的提问来源于stack exchange,提问作者Lson

