diff --git a/runtime/Java/src/org/antlr/v4/runtime/ANTLRParserListener.java b/runtime/Java/src/org/antlr/v4/runtime/ANTLRParserListener.java new file mode 100644 index 000000000..efbd9cea9 --- /dev/null +++ b/runtime/Java/src/org/antlr/v4/runtime/ANTLRParserListener.java @@ -0,0 +1,6 @@ +package org.antlr.v4.runtime; + +/** */ +public interface ANTLRParserListener { + public void error(RecognitionException msg); +} diff --git a/runtime/Java/src/org/antlr/v4/runtime/BaseRecognizer.java b/runtime/Java/src/org/antlr/v4/runtime/BaseRecognizer.java index d4643a8e3..2c7f73cc4 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/BaseRecognizer.java +++ b/runtime/Java/src/org/antlr/v4/runtime/BaseRecognizer.java @@ -32,10 +32,7 @@ import org.antlr.runtime.Token; import org.antlr.runtime.TokenStream; import org.antlr.v4.runtime.misc.LABitSet; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** A generic recognizer that can handle recognizers generated from * parser and tree grammars. This is all the parsing @@ -171,8 +168,6 @@ public abstract class BaseRecognizer { * 3. consume until token found in resynch set * 4. try to resume parsing * 5. next match() will reset errorRecovery mode - * - * If you override, make sure to update syntaxErrors if you care about that. */ public void reportError(RecognitionException e) { // if we've already reported an error and have not matched a token @@ -184,15 +179,7 @@ public abstract class BaseRecognizer { state.syntaxErrors++; // don't count spurious state.errorRecovery = true; - displayRecognitionError(this.getTokenNames(), e); - } - - public void displayRecognitionError(String[] tokenNames, - RecognitionException e) - { - String hdr = getErrorHeader(e); - String msg = getErrorMessage(e, tokenNames); - emitErrorMessage(hdr+" "+msg); + notifyListeners(e); } /** What error message should be generated for the various @@ -217,7 +204,8 @@ public abstract class BaseRecognizer { * Override this to change the message generated for one or more * exception types. */ - public String getErrorMessage(RecognitionException e, String[] tokenNames) { + public String getErrorMessage(RecognitionException e) { + String[] tokenNames = getTokenNames(); String msg = e.getMessage(); if ( e instanceof UnwantedTokenException ) { UnwantedTokenException ute = (UnwantedTokenException)e; @@ -337,11 +325,6 @@ public abstract class BaseRecognizer { return "'"+s+"'"; } - /** Override this method to change where error messages go */ - public void emitErrorMessage(String msg) { - System.err.println(msg); - } - /** Recover from an error found on the input stream. This is * for NoViableAlt and mismatched symbol exceptions. If you enable * single token insertion and deletion, this will usually not @@ -589,7 +572,7 @@ public abstract class BaseRecognizer { RecognitionException e = null; // if next token is what we are looking for then "delete" this token if ( mismatchIsUnwantedToken(ttype) ) { - e = new UnwantedTokenException(ttype, state.input); + e = new UnwantedTokenException(this, ttype); /* System.err.println("recoverFromMismatchedToken deleting "+ ((TokenStream)input).LT(1)+ @@ -607,12 +590,12 @@ public abstract class BaseRecognizer { // can't recover with single token deletion, try insertion if ( mismatchIsMissingToken(follow) ) { Object inserted = getMissingSymbol(e, ttype, follow); - e = new MissingTokenException(ttype, state.input, inserted); + e = new MissingTokenException(this, ttype, inserted); reportError(e); // report after inserting so AW sees the token in the exception return inserted; } // even that didn't work; must throw the exception - e = new MismatchedTokenException(ttype, state.input); + e = new MismatchedTokenException(this, ttype); throw e; } @@ -867,4 +850,52 @@ public abstract class BaseRecognizer { System.out.println(); } + /* In v3, programmers altered error messages by overriding + displayRecognitionError() and possibly getTokenErrorDisplay(). + They overrode emitErrorMessage(String) to change where the output goes. + + Now, in v4, we're going to use a listener mechanism. This makes it + easier for language applications to have parsers notify them + upon error without having to override the parsers. If you don't specify + a listener, ANTLR calls the v3 legacy displayRecognitionError() + method. All that does is format a message and call emitErrorMessage(). + Otherwise, your listener will receive RecognitionException + exceptions and you can do what ever you want with them including + reproducing the same behavior by calling the legacy methods. + (In v4, RecognitionException includes the recognizer object). + + Grammar tools can have a listeners without having to worry about + messing up the programmers' error handling. + */ + + public void displayRecognitionError(RecognitionException e) { + String hdr = getErrorHeader(e); + String msg = getErrorMessage(e); + emitErrorMessage(hdr+" "+msg); + } + + /** Override this method to change where error messages go */ + public void emitErrorMessage(String msg) { + System.err.println(msg); + } + + public void addListener(ANTLRParserListener pl) { + if ( state.listeners==null ) { + state.listeners = + Collections.synchronizedList(new ArrayList(2)); + } + if ( pl!=null ) state.listeners.add(pl); + } + public void removeListener(ANTLRParserListener pl) { state.listeners.remove(pl); } + public void removeListeners() { state.listeners.clear(); } + public List getListeners() { return state.listeners; } + + public void notifyListeners(RecognitionException re) { + if ( state.listeners==null || state.listeners.size()==0 ) { + // call legacy v3 func; this calls emitErrorMessage(String msg) + displayRecognitionError(re); + return; + } + for (ANTLRParserListener pl : state.listeners) pl.error(re); + } } diff --git a/runtime/Java/src/org/antlr/v4/runtime/EarlyExitException.java b/runtime/Java/src/org/antlr/v4/runtime/EarlyExitException.java index 965f7dda4..bff54b797 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/EarlyExitException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/EarlyExitException.java @@ -27,8 +27,6 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; - /** The recognizer did not match anything for a (..)+ loop. */ public class EarlyExitException extends RecognitionException { public int decisionNumber; @@ -36,8 +34,8 @@ public class EarlyExitException extends RecognitionException { /** Used for remote debugger deserialization */ public EarlyExitException() {;} - public EarlyExitException(int decisionNumber, IntStream input) { - super(input); + public EarlyExitException(BaseRecognizer recognizer, int decisionNumber) { + super(recognizer); this.decisionNumber = decisionNumber; } } diff --git a/runtime/Java/src/org/antlr/v4/runtime/FailedPredicateException.java b/runtime/Java/src/org/antlr/v4/runtime/FailedPredicateException.java index a4e8ccf6f..5e0b6d25f 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/FailedPredicateException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/FailedPredicateException.java @@ -27,8 +27,6 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; - /** A semantic predicate failed during validation. Validation of predicates * occurs when normally parsing the alternative just like matching a token. * Disambiguating predicate evaluation occurs when we hoist a predicate into @@ -41,11 +39,11 @@ public class FailedPredicateException extends RecognitionException { /** Used for remote debugger deserialization */ public FailedPredicateException() {;} - public FailedPredicateException(IntStream input, + public FailedPredicateException(BaseRecognizer recognizer, String ruleName, String predicateText) { - super(input); + super(recognizer); this.ruleName = ruleName; this.predicateText = predicateText; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/MismatchedNotSetException.java b/runtime/Java/src/org/antlr/v4/runtime/MismatchedNotSetException.java index 5a98bb4d7..9600295e7 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/MismatchedNotSetException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/MismatchedNotSetException.java @@ -27,15 +27,14 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; import org.antlr.v4.runtime.misc.LABitSet; public class MismatchedNotSetException extends MismatchedSetException { /** Used for remote debugger deserialization */ public MismatchedNotSetException() {;} - public MismatchedNotSetException(LABitSet expecting, IntStream input) { - super(expecting, input); + public MismatchedNotSetException(BaseRecognizer recognizer, LABitSet expecting) { + super(recognizer, expecting); } public String toString() { diff --git a/runtime/Java/src/org/antlr/v4/runtime/MismatchedRangeException.java b/runtime/Java/src/org/antlr/v4/runtime/MismatchedRangeException.java index 48b0a34d7..1c49fd35c 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/MismatchedRangeException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/MismatchedRangeException.java @@ -27,16 +27,14 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; - public class MismatchedRangeException extends RecognitionException { public int a,b; /** Used for remote debugger deserialization */ public MismatchedRangeException() {;} - public MismatchedRangeException(int a, int b, IntStream input) { - super(input); + public MismatchedRangeException(BaseRecognizer recognizer, int a, int b) { + super(recognizer); this.a = a; this.b = b; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/MismatchedSetException.java b/runtime/Java/src/org/antlr/v4/runtime/MismatchedSetException.java index 35e2a47ca..b0e38a4c6 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/MismatchedSetException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/MismatchedSetException.java @@ -27,7 +27,6 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; import org.antlr.v4.runtime.misc.LABitSet; public class MismatchedSetException extends RecognitionException { @@ -36,8 +35,8 @@ public class MismatchedSetException extends RecognitionException { /** Used for remote debugger deserialization */ public MismatchedSetException() {;} - public MismatchedSetException(LABitSet expecting, IntStream input) { - super(input); + public MismatchedSetException(BaseRecognizer recognizer, LABitSet expecting) { + super(recognizer); this.expecting = expecting; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/MismatchedTokenException.java b/runtime/Java/src/org/antlr/v4/runtime/MismatchedTokenException.java index 5319bb1cf..ab74e970c 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/MismatchedTokenException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/MismatchedTokenException.java @@ -27,7 +27,6 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; import org.antlr.runtime.Token; /** A mismatched char or Token or tree node */ @@ -37,8 +36,8 @@ public class MismatchedTokenException extends RecognitionException { /** Used for remote debugger deserialization */ public MismatchedTokenException() {;} - public MismatchedTokenException(int expecting, IntStream input) { - super(input); + public MismatchedTokenException(BaseRecognizer recognizer, int expecting) { + super(recognizer); this.expecting = expecting; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/MismatchedTreeNodeException.java b/runtime/Java/src/org/antlr/v4/runtime/MismatchedTreeNodeException.java index 2f53e216b..b58eb3a76 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/MismatchedTreeNodeException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/MismatchedTreeNodeException.java @@ -27,8 +27,6 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.tree.TreeNodeStream; - /** */ public class MismatchedTreeNodeException extends RecognitionException { @@ -37,8 +35,10 @@ public class MismatchedTreeNodeException extends RecognitionException { public MismatchedTreeNodeException() { } - public MismatchedTreeNodeException(int expecting, TreeNodeStream input) { - super(input); + public MismatchedTreeNodeException(BaseRecognizer recognizer, + int expecting) + { + super(recognizer); this.expecting = expecting; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/MissingTokenException.java b/runtime/Java/src/org/antlr/v4/runtime/MissingTokenException.java index 3bf3c4a7e..af8252a5c 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/MissingTokenException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/MissingTokenException.java @@ -27,8 +27,6 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; - /** We were expecting a token but it's not found. The current token * is actually what we wanted next. Used for tree node errors too. */ @@ -37,8 +35,8 @@ public class MissingTokenException extends MismatchedTokenException { /** Used for remote debugger deserialization */ public MissingTokenException() {;} - public MissingTokenException(int expecting, IntStream input, Object inserted) { - super(expecting, input); + public MissingTokenException(BaseRecognizer recognizer, int expecting, Object inserted) { + super(recognizer,expecting); this.inserted = inserted; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java b/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java index 584bc4e20..834ee48e2 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java @@ -28,7 +28,6 @@ package org.antlr.v4.runtime; import org.antlr.runtime.CharStream; -import org.antlr.runtime.IntStream; public class NoViableAltException extends RecognitionException { public String grammarDecisionDescription; @@ -38,19 +37,19 @@ public class NoViableAltException extends RecognitionException { /** Used for remote debugger deserialization */ public NoViableAltException() {;} - public NoViableAltException(String grammarDecisionDescription, + public NoViableAltException(BaseRecognizer recognizer, + String grammarDecisionDescription, int decisionNumber, - int stateNumber, - IntStream input) + int stateNumber) { - super(input); + super(recognizer); this.grammarDecisionDescription = grammarDecisionDescription; this.decisionNumber = decisionNumber; this.stateNumber = stateNumber; } public String toString() { - if ( input instanceof CharStream) { + if ( recognizer.state.input instanceof CharStream) { return "NoViableAltException('"+(char)getUnexpectedType()+"'@["+grammarDecisionDescription+"])"; } else { diff --git a/runtime/Java/src/org/antlr/v4/runtime/RecognitionException.java b/runtime/Java/src/org/antlr/v4/runtime/RecognitionException.java index 8ff9820ff..316a952db 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/RecognitionException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/RecognitionException.java @@ -67,8 +67,11 @@ import org.antlr.runtime.tree.TreeNodeStream; * figure out a fancy report. */ public class RecognitionException extends RuntimeException { + /** Who threw the exception? */ + public BaseRecognizer recognizer; + /** What input stream did the error occur in? */ - public transient IntStream input; + //public transient IntStream input; /** What is index of token/char were we looking at when the error occurred? */ public int index; @@ -106,8 +109,9 @@ public class RecognitionException extends RuntimeException { public RecognitionException() { } - public RecognitionException(IntStream input) { - this.input = input; + public RecognitionException(BaseRecognizer recognizer) { + this.recognizer = recognizer; + IntStream input = recognizer.state.input; this.index = input.index(); if ( input instanceof TokenStream ) { this.token = ((TokenStream)input).LT(1); @@ -127,6 +131,10 @@ public class RecognitionException extends RuntimeException { } } +// public RecognitionException(IntStream input) { +// this.input = input; +// } + protected void extractInformationFromTreeNodeStream(IntStream input) { TreeNodeStream nodes = (TreeNodeStream)input; this.node = nodes.LT(1); @@ -172,11 +180,11 @@ public class RecognitionException extends RuntimeException { /** Return the token type or char of the unexpected input element */ public int getUnexpectedType() { - if ( input instanceof TokenStream) { + if ( recognizer.state.input instanceof TokenStream) { return token.getType(); } - else if ( input instanceof TreeNodeStream ) { - TreeNodeStream nodes = (TreeNodeStream)input; + else if ( recognizer.state.input instanceof TreeNodeStream ) { + TreeNodeStream nodes = (TreeNodeStream)recognizer.state.input; TreeAdaptor adaptor = nodes.getTreeAdaptor(); return adaptor.getType(node); } diff --git a/runtime/Java/src/org/antlr/v4/runtime/RecognizerSharedState.java b/runtime/Java/src/org/antlr/v4/runtime/RecognizerSharedState.java index 6ef9f077b..5345eacc5 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/RecognizerSharedState.java +++ b/runtime/Java/src/org/antlr/v4/runtime/RecognizerSharedState.java @@ -30,6 +30,7 @@ package org.antlr.v4.runtime; import org.antlr.runtime.IntStream; import org.antlr.v4.runtime.misc.QStack; +import java.util.List; import java.util.Map; /** The set of fields needed by an abstract recognizer to recognize input @@ -77,6 +78,8 @@ public class RecognizerSharedState { */ public Map[] ruleMemo; + List listeners; + public RecognizerSharedState() { this.ctx = new QStack(); } diff --git a/runtime/Java/src/org/antlr/v4/runtime/UnwantedTokenException.java b/runtime/Java/src/org/antlr/v4/runtime/UnwantedTokenException.java index 8dc95c273..3235a0c1a 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/UnwantedTokenException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/UnwantedTokenException.java @@ -27,7 +27,6 @@ */ package org.antlr.v4.runtime; -import org.antlr.runtime.IntStream; import org.antlr.runtime.Token; /** An extra token while parsing a TokenStream */ @@ -35,8 +34,8 @@ public class UnwantedTokenException extends MismatchedTokenException { /** Used for remote debugger deserialization */ public UnwantedTokenException() {;} - public UnwantedTokenException(int expecting, IntStream input) { - super(expecting, input); + public UnwantedTokenException(BaseRecognizer recognizer, int expecting) { + super(recognizer, expecting); } public Token getUnexpectedToken() {