antlr 4 没有给出错误的正确行和位置

Antlr 4 does not give the correct line and position of the error

我在使用 Antlr 4 进行错误处理时遇到问题,当我在我的自定义侦听器中调用方法 notifyErrorListeners("msg") 时,Antlr 没有给我错误的确切行和位置。 这是我的 dsl 的一部分。

study( region = "region" , timestamp = 12:10 , processType = "processType")
...

我的问题是语法接受学习中变量的重复,例如。

study( region = "region" , region = "region" , timestamp = 12:10)
...

作为解决方案,我决定在我的自定义监听器中管理它。这是我的监听器

@Getter
public class StudyPatternListener extends StudyBaseListener {

     private Map<String, Object> studyParams = new HashMap<>();

     @Setter
     private StudyParser parser;

     @Override
     public void enterStudyAssignProcessType(StudyAssignProcessTypeContext ctx) {
        String processType = ctx.STRING().getText().replace("\"", "");
        if (!this.studyParams.containsKey("processType")) {
            this.studyParams.put("processType", processType);
        } else {
            // here Antlr doesn't give me the right line and the right position of the error.
            parser.notifyErrorListeners("processType parameter is already assigned");
        }
    }

这是我的自定义错误侦听器。

public class StudyErrorListener extends BaseErrorListener {

 @Override
 public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
                 String msg, RecognitionException e) {
        System.err.println("line = " + line + ", position = " + charPositionInLine + " , msg =  " + msg);
    }
}

这是我的语法。

grammar Study;

program: main EOF;  // the program rule.
// LINE_COMMENT* 
main:  NEWLINE* study  NEWLINE* ; // the main rule;
study : 'study' '(' studyParameters  ')';
studyParameters: (| ( studyAssign (',' studyAssign)*) ); // assign in the study

studyAssign: 'timestamp'            '='  TIMESTAMP  # studyAssignTimestamp
           | 'region'               '='  STRING     # studyAssignRegion
           | 'processType'          '='  STRING     # studyAssignProcessType
           | 'businessDate'         '='  DATE       # studyAssignBusinessDate
           ;                                        // valid study parameters .

fragment LETTER : [A-Za-z];
fragment DIGIT : [0-9];
fragment TWODIGIT : DIGIT DIGIT;
TIMESTAMP: TWODIGIT ':' TWODIGIT; 
DATE : TWODIGIT TWODIGIT ;
ID : LETTER+; 
STRING : '"' ( ~ '"' )* '"' ;
NEWLINE:'\r'? '\n' ;
LINE_COMMENT: '#' ~[\r\n]* -> skip;
WS  :   [ \t]+ -> skip ; 

在验证树遍历期间通过解析器错误侦听器返回报告将获取解析器的最后位置。不是你想要的(解析器将完成)。

需要从上下文节点中挖掘信息并转换为合理易懂的形式:

@Override
public void enterStudyAssignProcessType(StudyAssignProcessTypeContext ctx) {
    ... 
    // validate key/value pairs specified by an 'attribute' rule
    for (AttributeContext attribute : ctx.attribute()) {
        String id = attribute.id().getText();
        Props props = AttrMap.get(id);            // external map of allowed keys & values
        if (props.name.equals(KeyAttr.INVALID)) { // whatever error check(s)
            String cause = "Invalid name '" + id + "' at %s:%s[%s]";
            reportProblem(IMarker.SEVERITY_ERROR, Kind.ATTRIBUTE, attribute.id().name, cause);
        }
    }
}

private void reportProblem(int severity, Kind kind, String cause, Token... tokens) {
    Token beg = tokens[0];
    Token end = tokens[tokens.length - 1];
    errLine = beg.getLine();
    errPos = beg.getCharPositionInLine();
    errOffset = beg.getStartIndex();
    errLen = end.getStopIndex() - errOffset + 1;
    displayProblem(severity, kind, calcVisual(cause)); // your presentation
}

private String calcVisual(String cause) {
    int beg = errOffset - errPos;       // beg of line
    if (beg > 0) {
        String txt = Strings.EMPTY; // ""
        Interval range = Interval.of(beg, errOffset);
        try {
            txt = record.cs.getText(range);
            errVisPos = Strings.measureVisualWidth(txt, tabWidth); // adj for embedded tabs
        } catch (Exception e) {
            String name = ...; // source name
            Log.error(this, "Bad visual position %s: %s (%s: %s)", e.getMessage(), name, range, txt);
        }

        txt = Strings.EMPTY;
        range = Interval.of(beg, errOffset + errLen);
        try {
            txt = record.cs.getText(range);
            errVisLen = Strings.measureVisualWidth(txt, tabWidth) - errVisPos;
        } catch (Exception e) {
            String name = ...;
            Log.error(this, "Bad visual position %s: %s (%s: %s)", e.getMessage(), name, range, txt);
        }
    }

    return String.format(cause, errLine, errVisPos, errVisLen);
}