Antlr 4 не дает правильную строку и позицию ошибки - PullRequest
1 голос
/ 22 апреля 2020

У меня проблема с обработкой ошибок в 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 ; 

1 Ответ

1 голос
/ 22 апреля 2020

Отчетность через прослушиватель ошибок парсера во время обхода дерева валидации будет собирать последнюю позицию парсера. Не то, что вы хотите (синтаксический анализатор завершит работу).

Необходимо выкопать информацию из узла контекста и преобразовать ее в достаточно понятную форму:

@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);
}
...