diff --git a/runtime/Java/src/org/antlr/v4/runtime/LexerNoViableAltException.java b/runtime/Java/src/org/antlr/v4/runtime/LexerNoViableAltException.java index ffa59a80f..37189df30 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/LexerNoViableAltException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/LexerNoViableAltException.java @@ -37,12 +37,12 @@ public class LexerNoViableAltException extends RecognitionException { public int startIndex; /** Which configurations did we try at input.index() that couldn't match input.LA(1)? */ - public OrderedHashSet deadEndConfigs; + public ATNConfig[] deadEndConfigs; public LexerNoViableAltException(Lexer lexer, CharStream input, int startIndex, - OrderedHashSet deadEndConfigs) { + ATNConfig[] deadEndConfigs) { super(lexer, input, null); this.startIndex = startIndex; this.deadEndConfigs = deadEndConfigs; diff --git a/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java b/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java index 207294508..63529084c 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/NoViableAltException.java @@ -29,6 +29,7 @@ package org.antlr.v4.runtime; import org.antlr.v4.runtime.atn.ATNConfig; +import org.antlr.v4.runtime.misc.Nullable; import org.antlr.v4.runtime.misc.OrderedHashSet; /** The parser could not decide which path in the decision to take based @@ -36,7 +37,7 @@ import org.antlr.v4.runtime.misc.OrderedHashSet; */ public class NoViableAltException extends RecognitionException { /** Which configurations did we try at input.index() that couldn't match input.LT(1)? */ - public OrderedHashSet deadEndConfigs; + public ATNConfig[] deadEndConfigs; /** The token object at the start index; the input stream might * not be buffering tokens so get a reference to it. (At the @@ -59,7 +60,7 @@ public class NoViableAltException extends RecognitionException { Token startToken, Token offendingToken, Symbol offendingNode, - OrderedHashSet deadEndConfigs, + @Nullable ATNConfig[] deadEndConfigs, ParserRuleContext ctx) { super(recognizer, input, ctx); diff --git a/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java b/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java index 6c7489044..4f7247c4c 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/NoViableTreeGrammarAltException.java @@ -30,6 +30,7 @@ package org.antlr.v4.runtime; import org.antlr.v4.runtime.atn.ATNConfig; +import org.antlr.v4.runtime.misc.Nullable; import org.antlr.v4.runtime.misc.OrderedHashSet; import org.antlr.v4.runtime.tree.ASTNodeStream; import org.antlr.v4.runtime.tree.TreeParser; @@ -50,7 +51,7 @@ public class NoViableTreeGrammarAltException extends NoViableAltException { ASTNodeStream input, Symbol startNode, Symbol offendingNode, - OrderedHashSet deadEndConfigs, + @Nullable ATNConfig[] deadEndConfigs, ParserRuleContext ctx) { super(recognizer, input, input.getTreeAdaptor().getToken(startNode), diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java index 4d8019835..cc95287f7 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java @@ -38,6 +38,7 @@ import org.antlr.v4.runtime.misc.OrderedHashSet; import java.io.IOException; import java.io.OutputStream; +import java.util.Arrays; /** "dup" of ParserInterpreter */ public class LexerATNSimulator extends ATNSimulator { @@ -339,7 +340,7 @@ public class LexerATNSimulator extends ATNSimulator { if ( t==CharStream.EOF && input.index()==startIndex ) { return Token.EOF; } - throw new LexerNoViableAltException(recog, input, startIndex, reach); + throw new LexerNoViableAltException(recog, input, startIndex, reach.toArray(new ATNConfig[reach.size()])); } int ruleIndex = atnPrevAccept.config.state.ruleIndex; @@ -545,10 +546,10 @@ public class LexerATNSimulator extends ATNSimulator { if ( dfa_debug ) { System.out.format("no edge for %s\n", getTokenName(input.LA(1))); System.out.format("ATN exec upon %s at DFA state %d = %s\n", - input.substring(startIndex, input.index()), s.stateNumber, s.configs); + input.substring(startIndex, input.index()), s.stateNumber, Arrays.toString(s.configs)); } - int ttype = exec(input, s.configs); + int ttype = exec(input, new OrderedHashSet(s.configs)); if ( dfa_debug ) { System.out.format("back from DFA update, ttype=%d, dfa[mode %d]=\n%s\n", @@ -653,8 +654,9 @@ public class LexerATNSimulator extends ATNSimulator { if ( traversedPredicate ) return null; // cannot cache newState.stateNumber = dfa[mode].states.size(); - newState.configs = new OrderedHashSet(); - newState.configs.addAll(configs); + // Note: the configs array is copied in the DFAState ctor, no need to copy again + //newState.configs = new OrderedHashSet(); + //newState.configs.addAll(configs); dfa[mode].states.put(newState, newState); return newState; } 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 68346e35b..89dfd5327 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java @@ -202,7 +202,7 @@ public class ParserATNSimulator extends ATNSimulator { // start all over with ATN; can't use DFA input.seek(startIndex); DFA throwAwayDFA = new DFA(dfa.atnStartState); - int alt = execATN(input, throwAwayDFA, startIndex, s0.configs, false); + int alt = execATN(input, throwAwayDFA, startIndex, new OrderedHashSet(s0.configs), false); s.ctxToPrediction.put(outerContext, alt); return alt; } @@ -230,7 +230,7 @@ public class ParserATNSimulator extends ATNSimulator { " at DFA state "+s.stateNumber); } try { - alt = execATN(input, dfa, startIndex, s.configs, false); + alt = execATN(input, dfa, startIndex, new OrderedHashSet(s.configs), false); // this adds edge even if next state is accept for // same alt; e.g., s0-A->:s1=>2-B->:s2=>2 // TODO: This next stuff kills edge, but extra states remain. :( @@ -1033,8 +1033,9 @@ public class ParserATNSimulator extends ATNSimulator { DFAState newState = proposed; newState.stateNumber = dfa.states.size(); - newState.configs = new OrderedHashSet(); - newState.configs.addAll(configs); + // Note: the configs array is copied in the DFAState ctor, no need to copy again + //newState.configs = new OrderedHashSet(); + //newState.configs.addAll(configs); dfa.states.put(newState, newState); if ( debug ) System.out.println("adding new DFA state: "+newState); return newState; @@ -1100,7 +1101,16 @@ public class ParserATNSimulator extends ATNSimulator { @NotNull public NoViableAltException noViableAlt(@NotNull SymbolStream input, @NotNull ParserRuleContext outerContext, - @NotNull OrderedHashSet configs, + @NotNull Set configs, + int startIndex) + { + return noViableAlt(input, outerContext, configs.toArray(new ATNConfig[configs.size()]), startIndex); + } + + @NotNull + public NoViableAltException noViableAlt(@NotNull SymbolStream input, + @NotNull ParserRuleContext outerContext, + @NotNull ATNConfig[] configs, int startIndex) { if ( parser instanceof TreeParser) { diff --git a/runtime/Java/src/org/antlr/v4/runtime/dfa/DFAState.java b/runtime/Java/src/org/antlr/v4/runtime/dfa/DFAState.java index 85d1ec1db..64f39a1ad 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/dfa/DFAState.java +++ b/runtime/Java/src/org/antlr/v4/runtime/dfa/DFAState.java @@ -35,10 +35,7 @@ import org.antlr.v4.runtime.atn.SemanticContext; import org.antlr.v4.runtime.misc.Nullable; import org.antlr.v4.runtime.misc.OrderedHashSet; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** A DFA state represents a set of possible ATN configurations. * As Aho, Sethi, Ullman p. 117 says "The DFA uses its state @@ -69,7 +66,7 @@ public class DFAState { /** The set of ATN configurations (state,alt,context) for this DFA state */ @Nullable - public OrderedHashSet configs = new OrderedHashSet(); + public ATNConfig[] configs; /** edges[symbol] points to target of symbol */ @Nullable @@ -139,17 +136,21 @@ public class DFAState { public DFAState(int stateNumber) { this.stateNumber = stateNumber; } - public DFAState(OrderedHashSet configs) { this.configs = configs; } + public DFAState(OrderedHashSet configs) { this.configs = configs.elements().toArray(new ATNConfig[configs.size()]); } /** Get the set of all alts mentioned by all ATN configurations in this * DFA state. */ public Set getAltSet() { - // TODO (sam): what to do when configs==null? + if (configs == null) { + return null; + } + Set alts = new HashSet(); for (ATNConfig c : configs) { alts.add(c.alt); } + if ( alts.size()==0 ) return null; return alts; } @@ -167,11 +168,15 @@ public class DFAState { /** A decent hash for a DFA state is the sum of the ATN state/alt pairs. */ @Override public int hashCode() { - // TODO (sam): what to do when configs==null? + if (configs == null) { + return 0; + } + int h = 0; for (ATNConfig c : configs) { h += c.alt; } + return h; } @@ -190,9 +195,9 @@ public class DFAState { public boolean equals(Object o) { // compare set of ATN configurations in this set with other if ( this==o ) return true; + if (!(o instanceof DFAState)) return false; DFAState other = (DFAState)o; - // TODO (sam): what to do when configs==null? - boolean sameSet = this.configs.equals(other.configs); + boolean sameSet = this.configs == other.configs || (this.configs != null && Arrays.equals(this.configs, other.configs)); // System.out.println("DFAState.equals: "+configs+(sameSet?"==":"!=")+other.configs); return sameSet; } @@ -200,7 +205,7 @@ public class DFAState { @Override public String toString() { StringBuilder buf = new StringBuilder(); - buf.append(stateNumber + ":" + configs); + buf.append(stateNumber).append(":").append(Arrays.toString(configs)); if ( isAcceptState ) { buf.append("=>"); if ( predicates!=null ) { diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/OrderedHashSet.java b/runtime/Java/src/org/antlr/v4/runtime/misc/OrderedHashSet.java index ec6840f01..230469268 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/OrderedHashSet.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/OrderedHashSet.java @@ -29,10 +29,7 @@ package org.antlr.v4.runtime.misc; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; +import java.util.*; /** A HashMap that remembers the order that the elements were added. * You can alter the ith element with set(i,value) too :) Unique list. @@ -43,6 +40,13 @@ public class OrderedHashSet extends LinkedHashSet { /** Track the elements as they are added to the set */ protected List elements = new ArrayList(); + public OrderedHashSet() { + } + + public OrderedHashSet(T[] values) { + Collections.addAll(this, values); + } + public T get(int i) { return elements.get(i); } @@ -94,6 +98,12 @@ public class OrderedHashSet extends LinkedHashSet { @Override public boolean equals(Object o) { + if (this == o) { + return true; + } else if (!(o instanceof OrderedHashSet)) { + return false; + } + // System.out.print("equals " + this + ", " + o+" = "); boolean same = elements!=null && elements.equals(((OrderedHashSet)o).elements); // System.out.println(same); diff --git a/tool/src/org/antlr/v4/tool/DOTGenerator.java b/tool/src/org/antlr/v4/tool/DOTGenerator.java index 7efc1d160..9f480a2ce 100644 --- a/tool/src/org/antlr/v4/tool/DOTGenerator.java +++ b/tool/src/org/antlr/v4/tool/DOTGenerator.java @@ -32,6 +32,7 @@ package org.antlr.v4.tool; import org.antlr.v4.misc.Utils; import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.*; +import org.antlr.v4.runtime.misc.OrderedHashSet; import org.stringtemplate.v4.*; import java.util.*; @@ -106,25 +107,24 @@ public class DOTGenerator { protected String getStateLabel(DFAState s) { if ( s==null ) return "null"; - StringBuffer buf = new StringBuffer(250); + StringBuilder buf = new StringBuilder(250); buf.append('s'); buf.append(s.stateNumber); if ( s.isAcceptState ) { - buf.append("=>"+s.prediction); + buf.append("=>").append(s.prediction); } // if ( Tool.internalOption_ShowATNConfigsInDFA ) { if ( false ) { - Set alts = ((DFAState)s).getAltSet(); + Set alts = s.getAltSet(); if ( alts!=null ) { buf.append("\\n"); // separate alts List altList = new ArrayList(); altList.addAll(alts); Collections.sort(altList); - Set configurations = ((DFAState)s).configs; + Set configurations = new OrderedHashSet(s.configs); for (int altIndex = 0; altIndex < altList.size(); altIndex++) { - Integer altI = (Integer) altList.get(altIndex); - int alt = altI.intValue(); + int alt = altList.get(altIndex); if ( altIndex>0 ) { buf.append("\\n"); }