Ich habe eine ANTLR 4 Grammatik und baute daraus einen Lexer und Parser. Jetzt versuche ich, diesen Parser so zu instanziieren, dass er analysiert wird, bis er auf einen Fehler stößt. Wenn es zu einem Fehler kommt, sollte es nicht weiter analysieren, aber es sollte nützliche Informationen über das Problem liefern; idealerweise ein maschinenlesbarer Ort und eine von Menschen lesbare Nachricht. HierAbbruch auf Parse Fehler mit nützlichen Nachricht
ist das, was ich im Moment haben:
grammar Toy;
@parser::members {
public static void main(String[] args) {
for (String arg: args)
System.out.println(arg + " => " + parse(arg));
}
public static String parse(String code) {
ErrorListener errorListener = new ErrorListener();
CharStream cstream = new ANTLRInputStream(code);
ToyLexer lexer = new ToyLexer(cstream);
lexer.removeErrorListeners();
lexer.addErrorListener(errorListener);
TokenStream tstream = new CommonTokenStream(lexer);
ToyParser parser = new ToyParser(tstream);
parser.removeErrorListeners();
parser.addErrorListener(errorListener);
parser.setErrorHandler(new BailErrorStrategy());
try {
String res = parser.top().str;
if (errorListener.message != null)
return "Parsed, but " + errorListener.toString();
return res;
} catch (ParseCancellationException e) {
if (errorListener.message != null)
return "Failed, because " + errorListener.toString();
throw e;
}
}
static class ErrorListener extends BaseErrorListener {
String message = null;
int start = -2, stop = -2, line = -2;
@Override
public void syntaxError(Recognizer<?, ?> recognizer,
Object offendingSymbol,
int line,
int charPositionInLine,
String msg,
RecognitionException e) {
if (message != null) return;
if (offendingSymbol instanceof Token) {
Token t = (Token) offendingSymbol;
start = t.getStartIndex();
stop = t.getStopIndex();
} else if (recognizer instanceof ToyLexer) {
ToyLexer lexer = (ToyLexer)recognizer;
start = lexer._tokenStartCharIndex;
stop = lexer._input.index();
}
this.line = line;
message = msg;
}
@Override public String toString() {
return start + "-" + stop + " l." + line + ": " + message;
}
}
}
top returns [String str]: e* EOF {$str = "All went well.";};
e: 'a' 'b' | 'a' 'c' e;
speichern diese auf Toy.g
, dann diese Befehle versuchen:
> java -jar antlr-4.5.2-complete.jar Toy.g
> javac -cp antlr-4.5.2-complete.jar Toy*.java
> java -cp .:tools/antlr-4.5.2-complete.jar ToyParser ab acab acc axb abc
ab => All went well.
acab => All went well.
acc => Failed, because 2-2 l.1: no viable alternative at input 'c'
axb => Parsed, but 1-1 l.1: token recognition error at: 'x'
Exception in thread "main" org.antlr.v4.runtime.misc.ParseCancellationException
at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:90)
at org.antlr.v4.runtime.Parser.match(Parser.java:229)
at ToyParser.top(ToyParser.java:187)
at ToyParser.parse(ToyParser.java:95)
at ToyParser.main(ToyParser.java:80)
Caused by: org.antlr.v4.runtime.InputMismatchException
at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:85)
... 4 more
Zum einen glaube ich, dass ich bin mach schon zu viel. Wenn ich mir anschaue, wie viel Code ich für eine einfache und häufige Aufgabe geschrieben habe, kann ich nicht anders, als mich zu fragen, ob mir eine einfachere Lösung fehlt. Auf der anderen Seite scheint das aus zwei Gründen nicht genug zu sein. Erstens, obwohl es mir gelungen ist, einen Lexer-Fehler zu melden, hindern sie den Parser nicht daran, den verbleibenden Stream fortzusetzen. Dies wird durch die Parsed, but
Zeichenfolge für den Eingang axb
belegt. Und zweitens sind immer noch Fehler vorhanden, die nicht an den Fehler-Listener gemeldet werden, wie der Stack-Trace zeigt.
Wenn ich die BailErrorStrategy
nicht installieren, erhalte ich mehr Nutzleistung:
acc => Parsed, but 2-2 l.1: mismatched input 'c' expecting 'a'
axb => Parsed, but 1-1 l.1: token recognition error at: 'x'
abc => Parsed, but 2-2 l.1: extraneous input 'c' expecting {<EOF>, 'a'}
Gibt es eine Möglichkeit, diese Art von Fehlermeldungen zu erhalten, aber immer noch auf Fehler bürgen? Ich kann see from the sources, dass die extraneous input
Nachricht in der Tat von der DefaultErrorStrategy
generiert wird, offenbar nachdem es ausgearbeitet hat, wie es über die Behebung des Problems gehen würde. Sollte ich es tun lassen und dann Kaution, d. H. Meine eigene Variante von BailErrorStrategy
schreiben, die vor dem Werfen zu super ruft?
Danke! Das bedeutet, dass Sie die Formatierung der Fehlermeldungen selbst vornehmen müssen, oder? Schauen Sie sich die Quellen von 'DefaultErrorStrategy' an (https://github.com/antlr/antlr4/blob/4.5/runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy.Java) scheint es keine Möglichkeit zu geben, zwischen der Fehlerformatierung und dem Aufruf zur Benachrichtigung der Fehlerreporter zu gelangen. – MvG
@MvG leider ja. aber das ist die einzige zuverlässige Lösung, auf die ich gestoßen bin – vsminkov