Как я могу определить, вводит ли пользователь строку, которая не соответствует моим правилам ANTLR грамматики? - PullRequest
0 голосов
/ 18 апреля 2019

Я создаю систему компьютерной алгебры, которая будет принимать алгебраическое выражение и упрощать или дифференцировать его.

Как видно из следующего кода, вводится пользовательский ввод, но если это строка, которая выполняетне соответствует моим правилам грамматики, возникает ошибка, строка 1: 6 не соответствует вводу '' ожидая {'(', INT, VAR}, и программа продолжает работать.

Как бы я перехватил ошибку и остановилПрограмма запущена? Заранее спасибо за любую помощь.

Класс контроллера:

    public static void main(String[] args) throws IOException {
        String userInput = "x*x*x+";
        getAST(userInput);

    }

    public static AST getAST(String userInput) {
        ParseTree tree = null;
        ExpressionLexer lexer = null;
        ANTLRInputStream input = new ANTLRInputStream(userInput);
        try {
        lexer = new ExpressionLexer(input);
        }catch(Exception e) {
            System.out.println("Incorrect grammar");
        }
        System.out.println("Lexer created");

        CommonTokenStream tokens = new CommonTokenStream(lexer);
        System.out.println("Tokens created");
        ExpressionParser parser = new ExpressionParser(tokens);
        System.out.println("Tokens parsed");


        tree = parser.expr(); 

        System.out.println("Tree created");
        System.out.println(tree.toStringTree(parser)); // print LISP-style tree
        Trees.inspect(tree, parser);

        ParseTreeWalker walker = new ParseTreeWalker();
        ExpressionListener listener = new buildAST();
        walker.walk(listener, tree);

        listener.printAST();
        listener.extractExpression();

        return new AST();
    }
}

Моя грамматика:

grammar Expression;

@header {
package exprs;

}
@members {
    // This method makes the parser stop running if it encounters
    // invalid input and throw a RuntimeException.
    public void reportErrorsAsExceptions() {
        //removeErrorListeners();

        addErrorListener(new ExceptionThrowingErrorListener());
    }

    private static class ExceptionThrowingErrorListener extends BaseErrorListener {
        @Override
        public void syntaxError(Recognizer<?, ?> recognizer,
                Object offendingSymbol, int line, int charPositionInLine,
                String msg, RecognitionException e) {
            throw new RuntimeException(msg);
        }
    }
}
@rulecatch {
    // ANTLR does not generate its normal rule try/catch
    catch(RecognitionException e) {
        throw e;
    }
}

expr  : left=expr op=('*'|'/'|'^') right=expr 
      | left=expr op=('+'|'-') right=expr 
      | '(' expr ')' 
      | atom 
      ;

atom : INT|VAR;
INT   : ('0'..'9')+ ;
VAR   : ('a' .. 'z') | ('A' .. 'Z') | '_';

WS    : [ \t\r\n]+ -> skip ;

1 Ответ

0 голосов
/ 20 апреля 2019

Типичный анализ разбора с ANTLR4 состоит из 2 этапов:

  1. «Быстрый и грязный» прогон с режимом прогнозирования SLL, который выручает при первой обнаруженной синтаксической ошибке.
  2. Нормальный прогон с использованием режима прогнозирования LL, который пытается восстановиться после ошибок синтаксического анализатора. Этот второй шаг необходимо выполнить только в том случае, если на первом шаге произошла ошибка.

Первый шаг - это свободный анализ, который не разрешает определенные неоднозначности и, следовательно, может сообщить об ошибке, которая на самом деле не существует (при разрешении в режиме LL). Но первый шаг быстрее и обеспечивает более быстрый результат для синтаксически правильного ввода. Этот код (JS) показывает настройку:

    this.parser.removeErrorListeners();
    this.parser.addErrorListener(this.errorListener);

    this.parser.errorHandler = new BailErrorStrategy();
    this.parser.interpreter.setPredictionMode(PredictionMode.SLL);

    try {
        this.tree = this.parser.grammarSpec();
    } catch (e) {
        if (e instanceof ParseCancellationException) {
            this.tokenStream.seek(0);
            this.parser.reset();
            this.parser.errorHandler = new DefaultErrorStrategy();
            this.parser.interpreter.setPredictionMode(PredictionMode.LL);
            this.tree = this.parser.grammarSpec();
        } else {
            throw e;
        }
    }

Чтобы избежать любых попыток разрешения синтаксических ошибок на первом шаге, вы также должны установить BailErrorStrategy. Эта стратегия просто выдает ParseCancellationException в случае синтаксической ошибки (аналогично тому, как вы делаете в своем коде). Вы можете добавить свою собственную обработку в предложении catch, чтобы запросить у пользователя правильный ввод и повторить шаг разбора.

...