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

如何在解析单个表达式后触发解析错误?ANTLR语法问题

问题背景

我有如下ANTLR语法:

start :
    expression
;

expression
    :
    | dateOperatorExpression
    | numberOperatorExpression
    | stringOperatorExpression
    | methodBooleanExpression
    | doubleMethodOperatorExpression
    | numberInExpression
    | stringInExpression
    | bracketExpression
    | andExpression
    | orExpression
    | notExpression
    ;

numberInExpression:
    | WS? METHOD WS? IN WS? '{' WS? NUMBER (WS? ',' WS? NUMBER)* '}' WS?
    ;

stringInExpression:
    | WS? METHOD WS? IN WS? '{' WS? STRING (WS? ',' WS? STRING)* '}' WS?
    ;

dateOperatorExpression:
    | WS? DATE WS? OPERATOR WS? DATE WS?
    | WS? DATE WS? OPERATOR WS? METHOD WS?
    | WS? METHOD WS? OPERATOR WS? DATE WS?
    | WS? DATE WS? OPERATOR WS? NULLVALUE WS?
    | WS? NULLVALUE WS? OPERATOR WS? DATE WS?
    ;
numberOperatorExpression:
    | WS? NUMBER WS? OPERATOR WS? NUMBER WS?
    | WS? NUMBER WS? OPERATOR WS? METHOD WS?
    | WS? METHOD WS? OPERATOR WS? NUMBER WS?
    | WS? NUMBER WS? OPERATOR WS? NULLVALUE WS?
    | WS? NULLVALUE WS? OPERATOR WS? NUMBER WS?
    ;
stringOperatorExpression:
    | WS? STRING WS? OPERATOR WS? STRING WS?
    | WS? STRING WS? OPERATOR WS? METHOD WS?
    | WS? METHOD WS? OPERATOR WS? STRING WS?
    | WS? STRING WS? OPERATOR WS? NULLVALUE WS?
    | WS? NULLVALUE WS? OPERATOR WS? STRING WS?
    ;
doubleMethodOperatorExpression:
    | WS? METHOD WS? OPERATOR WS? METHOD WS?
    | WS? METHOD WS? OPERATOR WS? NULLVALUE WS?
    | WS? NULLVALUE WS? OPERATOR WS? METHOD WS?
    ;
methodBooleanExpression:
    | WS? METHOD WS? OPERATOR WS? BOOLEAN WS?
    | WS? BOOLEAN WS? OPERATOR WS? METHOD WS?
    ;

bracketExpression:
    | '(' WS? expression WS? ')'
    ;
andExpression
    :
    |  AND WS? '(' expression (',' expression)* WS? ')'
    ;
orExpression
    :
    | OR WS? '(' expression (',' expression)* WS? ')'
    ;
notExpression
    :
    | NOT WS? expression
    ;

WS: (' ' | '\t' | '\r' | '\n')+ -> skip;
AND: 'AND' | 'and';
OR: 'OR' | 'or';
NOT: 'NOT' | 'not' | '!';
IN: 'IN' | 'in';

OPERATOR: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'ILIKE' | 'ilike' | 'LIKE' | 'like';

NULLVALUE: 'null' | 'NULL';
BOOLEAN: 'true' | 'false' | 'TRUE' | 'FALSE';
METHOD: [a-zA-Z_][a-zA-Z0-9_.]*;
NUMBER: [0-9.]+;
STRING: '&quot;' [a-zA-Z0-9%]+ '&quot;';
DATE: [0-9][0-9][0-9][0-9][-][0-9][0-9][-][0-9][0-9]('T'[0-9][0-9]':'[0-9][0-9]':'[0-9][0-9])?;

该语法支持类似如下的查询语句:

and((retired == true), or ((age >= 25), not(father.address.street != null)), firstName in {&quot;John&quot;, &quot;Pete&quot;})

为避免用户输入错误(比如误加多余括号时,解析器仅处理and((retired == true))并忽略剩余内容),我尝试将start规则修改为:

start :
   (expression)+
;

但出现错误提示:rule start contains a closure with at least one alternative that can match an empty string。请问为何会出现此错误?如何实现解析恰好一个表达式后触发错误的需求?


错误原因分析

出现这个错误的核心原因是你的expression规则(以及所有子规则)定义了空匹配分支。

看expression的定义:

expression
    :
    | dateOperatorExpression
    | ... // 其他分支
    ;

第一个:后面直接跟|,这意味着expression存在一个可以匹配空字符串的备选分支。当你把start改成(expression)+时,ANTLR会检测到这个闭包(+表示至少一次重复)可能陷入无限循环——因为空字符串可以被无限次匹配,而不需要消耗任何输入。这种歧义会触发ANTLR的语法检查错误。

所有子规则(比如numberInExpression、dateOperatorExpression等)也都存在同样的空分支问题,它们都能匹配空字符串,进一步加剧了这个问题。


解决方案

要实现“解析恰好一个表达式,多余内容直接报错”的需求,需要分两步操作:

1. 修复所有规则的空分支

把每个规则中开头的空分支去掉——也就是删除:后面的第一个|。例如:

  • 原expression规则:
    expression
        :
        | dateOperatorExpression
        | ...
        ;
    
    修改后:
    expression
        : dateOperatorExpression
        | numberOperatorExpression
        | stringOperatorExpression
        | methodBooleanExpression
        | doubleMethodOperatorExpression
        | numberInExpression
        | stringInExpression
        | bracketExpression
        | andExpression
        | orExpression
        | notExpression
        ;
    
  • 原numberInExpression规则:
    numberInExpression:
        | WS? METHOD WS? IN WS? '{' WS? NUMBER (WS? ',' WS? NUMBER)* '}' WS?
        ;
    
    修改后:
    numberInExpression:
        WS? METHOD WS? IN WS? '{' WS? NUMBER (WS? ',' WS? NUMBER)* '}' WS?
        ;
    

所有其他子规则都需要做同样的修改,确保每个规则只能匹配非空的有效内容。

2. 让start规则匹配到输入结束

将start规则改为匹配一个表达式 + 输入结束标记(EOF),这样如果输入中存在表达式之外的多余内容,解析器会直接报错:

start : expression EOF;

这样修改后,当用户输入多余的括号或其他内容时,解析器会因为无法匹配到EOF而抛出错误,完美解决你之前遇到的“忽略剩余内容”的问题。


内容的提问来源于stack exchange,提问作者Yves V.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:42:09