diff --git a/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRErrorStrategy.java b/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRErrorStrategy.java index 502a05351..fdb9c5c64 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRErrorStrategy.java +++ b/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRErrorStrategy.java @@ -181,7 +181,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy { public void reportNoViableAlternative(BaseRecognizer recognizer, NoViableAltException e) - throws RecognitionException + throws RecognitionException { TokenStream tokens = (TokenStream)recognizer.getInputStream(); String input = tokens.toString(e.startToken, e.offendingToken); diff --git a/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRTreeGrammarErrorStrategy.java b/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRTreeGrammarErrorStrategy.java index 96c7a4a65..286369af8 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRTreeGrammarErrorStrategy.java +++ b/runtime/Java/src/org/antlr/v4/runtime/DefaultANTLRTreeGrammarErrorStrategy.java @@ -32,7 +32,9 @@ package org.antlr.v4.runtime; import org.antlr.v4.runtime.tree.*; import org.antlr.v4.runtime.tree.gui.TreeViewer; -public class DefaultANTLRTreeGrammarErrorStrategy extends DefaultANTLRErrorStrategy { +import java.util.*; + +public class DefaultANTLRTreeGrammarErrorStrategy extends DefaultANTLRErrorStrategy { @Override public void beginErrorCondition(BaseRecognizer recognizer) { } @@ -42,25 +44,74 @@ public class DefaultANTLRTreeGrammarErrorStrategy extends DefaultANTLRErrorStrat throws RecognitionException { super.reportError(recognizer, e); - ASTNodeStream input = ((TreeParser) recognizer).getInputStream(); + TreeParser parser = (TreeParser)recognizer; + ASTNodeStream input = parser.getInputStream(); Object root = input.getTreeSource(); - if ( root instanceof AST ) { + // If instanceof Tree, we can show in TreeViewer + if ( root instanceof Tree ) { TreeViewer viewer = new TreeViewer(recognizer, (Tree)root); viewer.open(); + List unmatchedNodes = null; if ( e instanceof NoViableTreeGrammarAltException ) { NoViableTreeGrammarAltException nva = (NoViableTreeGrammarAltException)e; -// ((AST)nva.startNode).get + unmatchedNodes = getNodeList(input, nva); } else { - + unmatchedNodes = new ArrayList(); + unmatchedNodes.add((T)e.offendingNode); } -// input.get() -// viewer.addHighlightedNodes(); - // TODO: highlight error node + viewer.setHighlightedBoxColor(TreeViewer.LIGHT_RED); + viewer.addHighlightedNodes((List)unmatchedNodes); } } + @Override + public void reportNoViableAlternative(BaseRecognizer recognizer, + NoViableAltException e) + throws RecognitionException + { + TreeParser parser = (TreeParser)recognizer; + ASTNodeStream input = parser.getInputStream(); + List unmatchedNodes = + getNodeList(input, (NoViableTreeGrammarAltException)e); + StringBuffer buf = new StringBuffer(); + ASTAdaptor adap = input.getTreeAdaptor(); + for (int i = 0; i < unmatchedNodes.size(); i++) { + if ( i>0 ) buf.append(" "); + T t = unmatchedNodes.get(i); + buf.append(adap.getText(t)); + } + String s = buf.toString(); + String msg = "no viable alternative at node(s) "+escapeWSAndQuote(s); + recognizer.notifyListeners(e.offendingToken, msg, e); + } + + protected List getNodeList(ASTNodeStream input, + NoViableTreeGrammarAltException nva) + { + List unmatchedNodes; + T start = (T)nva.startNode; + T stop = (T)nva.offendingNode; + if ( input instanceof BufferedASTNodeStream) { + BufferedASTNodeStream b = + (BufferedASTNodeStream)input; + unmatchedNodes = b.get(start, stop); + } + else { + // if not buffered then we can't get from start to stop; + // just highlight the start/stop nodes, but not in between + unmatchedNodes = new ArrayList(); + if ( nva.startNode!=null ) { + unmatchedNodes.add((T)nva.startNode); + } + if ( nva.startNode==null || nva.offendingNode!=nva.startNode ) { + unmatchedNodes.add((T)nva.offendingNode); + } + } + return unmatchedNodes; + } + @Override public Object recoverInline(BaseRecognizer recognizer) throws RecognitionException { InputMismatchException e = new InputMismatchException(recognizer); @@ -70,9 +121,7 @@ public class DefaultANTLRTreeGrammarErrorStrategy extends DefaultANTLRErrorStrat @Override public void recover(BaseRecognizer recognizer, RecognitionException e) { - throw new RecognitionException(recognizer, - recognizer.getInputStream(), - recognizer._ctx); + throw new RuntimeException(e); } @Override diff --git a/runtime/Java/src/org/antlr/v4/runtime/Lexer.java b/runtime/Java/src/org/antlr/v4/runtime/Lexer.java index cb5cc2b6d..0059d1a6b 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/Lexer.java +++ b/runtime/Java/src/org/antlr/v4/runtime/Lexer.java @@ -136,7 +136,7 @@ public abstract class Lexer extends Recognizer } catch (LexerNoViableAltException e) { notifyListeners(e); // report error - recover(); + recover(e); ttype = SKIP; } if ( input.LA(1)==CharStream.EOF ) { @@ -301,7 +301,7 @@ public abstract class Lexer extends Recognizer return null; } - public void recover() { + public void recover(LexerNoViableAltException e) { _interp.consume(input); // skip a char and try again } diff --git a/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java b/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java index 96369f4e3..54d664d8b 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java @@ -38,16 +38,24 @@ public class NoViableTreeGrammarAltException extends NoViableAltException { protected Object offendingNode; public NoViableTreeGrammarAltException(BaseRecognizer recognizer) { - super(recognizer); + this(recognizer, + (ASTNodeStream)recognizer.getInputStream(), + recognizer.getCurrentInputSymbol(), + recognizer.getCurrentInputSymbol(), + null, + recognizer._ctx); } public NoViableTreeGrammarAltException(BaseRecognizer recognizer, - ASTNodeStream input, + ASTNodeStream input, Object startNode, Object offendingNode, OrderedHashSet deadEndConfigs, RuleContext ctx) { - super(recognizer, input, null, null, deadEndConfigs, ctx); + super(recognizer, input, + input.getTreeAdaptor().getToken(startNode), + input.getTreeAdaptor().getToken(offendingNode), + deadEndConfigs, ctx); this.startNode = startNode; this.offendingNode = offendingNode; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java b/runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java index 752ac8456..ef6cf5db1 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java @@ -852,15 +852,20 @@ public class ParserATNSimulator extends ATNSimulator { public int throwNoViableAlt(ObjectStream input, RuleContext outerContext, OrderedHashSet configs, int startIndex) { + Object startNode = null; + if ( input instanceof BufferedASTNodeStream ) { + startNode = input.get(startIndex); + } if ( parser instanceof TreeParser) { - throw new NoViableTreeGrammarAltException(parser, (ASTNodeStream)input, - input.get(startIndex), + throw new NoViableTreeGrammarAltException(parser, + (ASTNodeStream)input, + startNode, input.LT(1), configs, outerContext); } else { throw new NoViableAltException(parser, input, - (Token)input.get(startIndex), + (Token) startNode, (Token)input.LT(1), configs, outerContext); } diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/ASTAdaptor.java b/runtime/Java/src/org/antlr/v4/runtime/tree/ASTAdaptor.java index 780bffcc1..a8bb8d680 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/ASTAdaptor.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/ASTAdaptor.java @@ -42,17 +42,18 @@ import java.util.List; public interface ASTAdaptor { // BEGIN new v4 stuff - // If not null root, add kids to it - //public void addChildren(Object root, List kids); - - //public List getChildren(Object root); - /** Used to track elements to left of -> for use in rewrite. These * are some kind of trees, but we generically use Object * for tree types in ANTLR. */ public List createElementList(); + /** Get the absolute index starting from 0 of this note within + * a node stream. This should w +ork even if the stream is not buffered + */ +// public int getNodeIndex(T t); + // END new v4 stuff // C o n s t r u c t i o n diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/BufferedASTNodeStream.java b/runtime/Java/src/org/antlr/v4/runtime/tree/BufferedASTNodeStream.java index 2b7ad4796..bff469c3c 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/BufferedASTNodeStream.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/BufferedASTNodeStream.java @@ -213,6 +213,29 @@ public class BufferedASTNodeStream implements ASTNodeStream { return nodes.get(i); } + public List get(int i, int j) { + if ( p==-1 ) { + fillBuffer(); + } + return nodes.subList(i,j+1); + } + + public List get(T start, T stop) { + int i=0; + for (; i=nodes.size() ) return null; + if ( j>=nodes.size() ) j = nodes.size()-1; + return get(i,j); + } + public T LT(int k) { if ( p==-1 ) { fillBuffer(); diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/CommonASTAdaptor.java b/runtime/Java/src/org/antlr/v4/runtime/tree/CommonASTAdaptor.java index 5305766fd..5166a7a86 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/CommonASTAdaptor.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/CommonASTAdaptor.java @@ -130,6 +130,7 @@ public class CommonASTAdaptor extends BaseASTAdaptor { * override this in your own adaptor. */ public Token getToken(CommonAST t) { + if ( t==null ) return null; return t.getToken(); } diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/TreeParser.java b/runtime/Java/src/org/antlr/v4/runtime/tree/TreeParser.java index 3f7d5a5fa..e729af4b1 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/TreeParser.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/TreeParser.java @@ -41,7 +41,7 @@ public class TreeParser extends BaseRecognizer { public static final int DOWN = Token.DOWN; public static final int UP = Token.UP; - public ASTAdaptor _adaptor = new CommonASTAdaptor(); + public ASTAdaptor _adaptor = (ASTAdaptor)new CommonASTAdaptor(); // precompiled regex used by inContext static String dotdot = ".*[^.]\\.\\.[^.].*"; @@ -53,7 +53,7 @@ public class TreeParser extends BaseRecognizer { public TreeParser(ASTNodeStream input) { super(input); - _errHandler = new DefaultANTLRTreeGrammarErrorStrategy(); + _errHandler = new DefaultANTLRTreeGrammarErrorStrategy(); } public void reset() { diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/gui/TreeViewer.java b/runtime/Java/src/org/antlr/v4/runtime/tree/gui/TreeViewer.java index efc2e0e51..4b23917f8 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/gui/TreeViewer.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/gui/TreeViewer.java @@ -232,6 +232,7 @@ public class TreeViewer extends JComponent { ok.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); dialog.dispose(); } } diff --git a/tool/playground/E.g b/tool/playground/E.g index 7a0663faa..a8d1af2e3 100644 --- a/tool/playground/E.g +++ b/tool/playground/E.g @@ -1,5 +1,12 @@ grammar E; +@lexer::members { +public void recover(LexerNoViableAltException e) { + super.recover(e); + throw new RuntimeException(e); +} +} + prog: classDef+ ; // match one or more class definitions classDef diff --git a/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg b/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg index 11a2c6101..095ffd9e0 100644 --- a/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg +++ b/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg @@ -188,12 +188,10 @@ RuleFunction(currentRule,code,locals,ruleCtx,altLabelCtxs,namedActions,finallyAc } - catch (RecognitionException re) { _errHandler.reportError(this, re); _errHandler.recover(this, re); } - finally { exitRule(RULE_); @@ -378,6 +376,14 @@ case : Sync(s) ::= "sync();" ThrowNoViableAlt(t) ::= "throw new NoViableAltException(this);" +ThrowNoViableTreeAlt(t) ::= << +/* +NoViableTreeGrammarAltException e = new NoViableTreeGrammarAltException(this); +_errHandler.reportError(this, e); +throw e; +*/ +throw new NoViableTreeGrammarAltException(this); +>> TestSetInline(s) ::= << ==}; separator=" || "> diff --git a/tool/src/org/antlr/v4/codegen/model/Choice.java b/tool/src/org/antlr/v4/codegen/model/Choice.java index 39651d3be..7f125ff0f 100644 --- a/tool/src/org/antlr/v4/codegen/model/Choice.java +++ b/tool/src/org/antlr/v4/codegen/model/Choice.java @@ -30,14 +30,12 @@ package org.antlr.v4.codegen.model; import org.antlr.v4.codegen.OutputModelFactory; -import org.antlr.v4.codegen.model.decl.Decl; -import org.antlr.v4.codegen.model.decl.TokenTypeDecl; +import org.antlr.v4.codegen.model.decl.*; import org.antlr.v4.misc.Utils; import org.antlr.v4.runtime.misc.IntervalSet; import org.antlr.v4.tool.ast.GrammarAST; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** The class hierarchy underneath SrcOp is pretty deep but makes sense that, * for example LL1StarBlock is a kind of LL1Loop which is a kind of Choice. @@ -89,4 +87,15 @@ public abstract class Choice extends RuleElement { } return expr; } + + public ThrowNoViableAlt getThrowNoViableAlt(OutputModelFactory factory, + GrammarAST blkAST, + IntervalSet expecting) { + if ( factory.getGrammar().isTreeGrammar() ) { + return new ThrowNoViableTreeAlt(factory, blkAST, expecting); + } + else { + return new ThrowNoViableAlt(factory, blkAST, expecting); + } + } } diff --git a/tool/src/org/antlr/v4/codegen/model/LL1AltBlock.java b/tool/src/org/antlr/v4/codegen/model/LL1AltBlock.java index 4c12444c7..b2cf59ca6 100644 --- a/tool/src/org/antlr/v4/codegen/model/LL1AltBlock.java +++ b/tool/src/org/antlr/v4/codegen/model/LL1AltBlock.java @@ -48,7 +48,7 @@ public class LL1AltBlock extends LL1Choice { altLook = getAltLookaheadAsStringLists(altLookSets); IntervalSet expecting = IntervalSet.or(altLookSets); // combine alt sets - this.error = new ThrowNoViableAlt(factory, blkAST, expecting); + this.error = getThrowNoViableAlt(factory, blkAST, expecting); System.out.println(blkAST.toStringTree()+" LL1AltBlock expecting="+expecting); } } diff --git a/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlockSingleAlt.java b/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlockSingleAlt.java index 06703dcb0..2a5538130 100644 --- a/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlockSingleAlt.java +++ b/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlockSingleAlt.java @@ -56,7 +56,7 @@ public class LL1OptionalBlockSingleAlt extends LL1Choice { IntervalSet followLook = altLookSets[2]; IntervalSet expecting = (IntervalSet)look.or(followLook); - this.error = new ThrowNoViableAlt(factory, blkAST, expecting); + this.error = getThrowNoViableAlt(factory, blkAST, expecting); System.out.println(blkAST.toStringTree()+" LL1OptionalBlockSingleAlt expecting="+expecting); expr = addCodeForLookaheadTempVar(look); diff --git a/tool/src/org/antlr/v4/codegen/model/LL1PlusBlock.java b/tool/src/org/antlr/v4/codegen/model/LL1PlusBlock.java index e837b60ac..eebb3ba7c 100644 --- a/tool/src/org/antlr/v4/codegen/model/LL1PlusBlock.java +++ b/tool/src/org/antlr/v4/codegen/model/LL1PlusBlock.java @@ -29,8 +29,7 @@ package org.antlr.v4.codegen.model; -import org.antlr.v4.codegen.CodeGenerator; -import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.*; import org.antlr.v4.runtime.atn.PlusBlockStartState; import org.antlr.v4.runtime.misc.IntervalSet; import org.antlr.v4.tool.Grammar; @@ -67,7 +66,7 @@ public class LL1PlusBlock extends LL1Loop { IntervalSet all = new IntervalSet(); for (IntervalSet s : altLookSets) all.addAll(s); - this.error = new ThrowNoViableAlt(factory, plusRoot, all); + this.error = getThrowNoViableAlt(factory, plusRoot, all); loopExpr = addCodeForLoopLookaheadTempVar(all); diff --git a/tool/src/org/antlr/v4/codegen/model/PlusBlock.java b/tool/src/org/antlr/v4/codegen/model/PlusBlock.java index bb34775be..10b2e8fd1 100644 --- a/tool/src/org/antlr/v4/codegen/model/PlusBlock.java +++ b/tool/src/org/antlr/v4/codegen/model/PlusBlock.java @@ -49,7 +49,7 @@ public class PlusBlock extends Loop { stateNumber = blkStart.loopBackState.stateNumber; blockStartStateNumber = blkStart.stateNumber; loopBackStateNumber = loop.stateNumber; - this.error = new ThrowNoViableAlt(factory, plusRoot, null); + this.error = getThrowNoViableAlt(factory, plusRoot, null); decision = loop.decision; exitAlt = alts.size()+1; } diff --git a/tool/src/org/antlr/v4/codegen/model/RuleFunction.java b/tool/src/org/antlr/v4/codegen/model/RuleFunction.java index bdba7c761..ac51a46ce 100644 --- a/tool/src/org/antlr/v4/codegen/model/RuleFunction.java +++ b/tool/src/org/antlr/v4/codegen/model/RuleFunction.java @@ -52,7 +52,6 @@ public class RuleFunction extends OutputModelObject { public int index; public Collection args = null; public Rule rule; - public boolean catch_; @ModelElement public List code; @ModelElement public OrderedHashSet locals; // TODO: move into ctx? @@ -74,8 +73,6 @@ public class RuleFunction extends OutputModelObject { index = r.index; - catch_ = !r.g.isTreeGrammar(); - ruleCtx = r.g.isTreeGrammar() ? new TreeParserStructDecl(factory, r) : new StructDecl(factory, r); diff --git a/tool/src/org/antlr/v4/codegen/model/ThrowNoViableTreeAlt.java b/tool/src/org/antlr/v4/codegen/model/ThrowNoViableTreeAlt.java new file mode 100644 index 000000000..488ab5295 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ThrowNoViableTreeAlt.java @@ -0,0 +1,43 @@ +/* + [The "BSD license"] + Copyright (c) 2011 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.antlr.v4.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +public class ThrowNoViableTreeAlt extends ThrowNoViableAlt { + public ThrowNoViableTreeAlt(OutputModelFactory factory, + GrammarAST blkOrEbnfRootAST, + IntervalSet expecting) + { + super(factory, blkOrEbnfRootAST, expecting); + } +}