reorged a bit; improved msg about recur ovf.

[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 6768]
This commit is contained in:
parrt 2010-03-23 12:07:29 -08:00
parent a87f6e7666
commit 8650c29627
12 changed files with 130 additions and 267 deletions

View File

@ -276,8 +276,8 @@ ANALYSIS_TIMEOUT(arg) ::= <<
ANTLR could not analyze this decision in rule <arg.enclosingRule>; often this is because of recursive rule references visible from the left edge of alternatives. ANTLR will re-analyze the decision with a fixed lookahead of k=1. Consider using "options {k=1;}" for that decision and possibly adding a syntactic predicate ANTLR could not analyze this decision in rule <arg.enclosingRule>; often this is because of recursive rule references visible from the left edge of alternatives. ANTLR will re-analyze the decision with a fixed lookahead of k=1. Consider using "options {k=1;}" for that decision and possibly adding a syntactic predicate
>> >>
RECURSION_OVERFLOW(alt,input,targetRules,callSiteStates) ::= << RECURSION_OVERFLOW(arg) ::= <<
Alternative <alt>: after matching input such as <input> decision cannot predict what comes next due to recursion overflow <targetRules,callSiteStates:{t,c|to <t> from <c:{s|<s.enclosingRule.name>};separator=", ">}; separator=" and "> Recursion overflow to <arg.targetRule.name> from alternative <arg.alt> of <arg.sourceRule.name> after matching input such as <arg.input>
>> >>
LEFT_RECURSION_CYCLES(arg) ::= << LEFT_RECURSION_CYCLES(arg) ::= <<

View File

@ -21,26 +21,26 @@ public class Tool {
public String VERSION = "!Unknown version!"; public String VERSION = "!Unknown version!";
//public static final String VERSION = "${project.version}"; //public static final String VERSION = "${project.version}";
public static final String UNINITIALIZED_DIR = "<unset-dir>"; public static final String UNINITIALIZED_DIR = "<unset-dir>";
private List<String> grammarFileNames = new ArrayList<String>(); public List<String> grammarFileNames = new ArrayList<String>();
private boolean generate_NFA_dot = false; public boolean generate_NFA_dot = false;
private boolean generate_DFA_dot = false; public boolean generate_DFA_dot = false;
private String outputDirectory = "."; public String outputDirectory = ".";
private boolean haveOutputDir = false; public boolean haveOutputDir = false;
private String inputDirectory = null; public String inputDirectory = null;
private String parentGrammarDirectory; public String parentGrammarDirectory;
private String grammarOutputDirectory; public String grammarOutputDirectory;
private boolean haveInputDir = false; public boolean haveInputDir = false;
private String libDirectory = "."; public String libDirectory = ".";
private boolean debug = false; public boolean debug = false;
private boolean trace = false; public boolean trace = false;
private boolean profile = false; public boolean profile = false;
private boolean report = false; public boolean report = false;
private boolean printGrammar = false; public boolean printGrammar = false;
private boolean depend = false; public boolean depend = false;
private boolean forceAllFilesToOutputDir = false; public boolean forceAllFilesToOutputDir = false;
private boolean forceRelativeOutput = false; public boolean forceRelativeOutput = false;
protected boolean deleteTempLexer = true; public boolean deleteTempLexer = true;
private boolean verbose = false; public boolean verbose = false;
/** Don't process grammar file if generated files are newer than grammar */ /** Don't process grammar file if generated files are newer than grammar */
/** /**
* Indicate whether the tool should analyze the dependencies of the provided grammar * Indicate whether the tool should analyze the dependencies of the provided grammar
@ -57,11 +57,11 @@ public class Tool {
* *
* @param make * @param make
*/ */
private boolean make = false; public boolean make = false;
private boolean showBanner = true; public boolean showBanner = true;
/** Exit after showing version or whatever */ /** Exit after showing version or whatever */
private static boolean exitNow = false; public static boolean exitNow = false;
// The internal options are for my use on the command line during dev // The internal options are for my use on the command line during dev
public static boolean internalOption_PrintGrammarTree = false; public static boolean internalOption_PrintGrammarTree = false;
@ -507,20 +507,26 @@ public class Tool {
public void generateDFAs(Grammar g) { public void generateDFAs(Grammar g) {
for (DFA dfa : g.decisionDFAs.values()) { for (DFA dfa : g.decisionDFAs.values()) {
DOTGenerator dotGenerator = new DOTGenerator(g); generateDFA(g, dfa);
String dot = dotGenerator.getDOT(dfa.startState); }
String dotFileName = g.name + "." + "dec-" + dfa.decision; }
if (g.implicitLexer!=null) {
dotFileName = g.name + public void generateDFA(Grammar g, DFA dfa) {
Grammar.getGrammarTypeToFileNameSuffix(g.getType()) + DOTGenerator dotGenerator = new DOTGenerator(g);
"." + "dec-" + dfa.decision; String dot = dotGenerator.getDOT(dfa.startState);
} String dec = "dec-";
try { //if ( dfa.minimized ) dec += "min-";
writeDOTFile(g, dotFileName, dot); String dotFileName = g.name + "." + dec + dfa.decision;
} if (g.implicitLexer!=null) {
catch (IOException ioe) { dotFileName = g.name +
ErrorManager.toolError(ErrorType.CANNOT_WRITE_FILE, dotFileName, ioe); Grammar.getGrammarTypeToFileNameSuffix(g.getType()) +
} "." + dec + dfa.decision;
}
try {
writeDOTFile(g, dotFileName, dot);
}
catch (IOException ioe) {
ErrorManager.toolError(ErrorType.CANNOT_WRITE_FILE, dotFileName, ioe);
} }
} }
@ -649,11 +655,11 @@ public class Tool {
return outputDir; return outputDir;
} }
private static void version() { public static void version() {
ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION); ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION);
} }
private static void help() { public static void help() {
ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION); ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION);
System.err.println("usage: java org.antlr.Tool [args] file.g [file2.g file3.g ...]"); System.err.println("usage: java org.antlr.Tool [args] file.g [file2.g file3.g ...]");
System.err.println(" -o outputDir specify output directory where all output is generated"); System.err.println(" -o outputDir specify output directory where all output is generated");
@ -673,7 +679,7 @@ public class Tool {
System.err.println(" -X display extended argument list"); System.err.println(" -X display extended argument list");
} }
private static void Xhelp() { public static void Xhelp() {
ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION); ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION);
System.err.println(" -Xgrtree print the grammar AST"); System.err.println(" -Xgrtree print the grammar AST");
System.err.println(" -Xdfa print DFA as text "); System.err.println(" -Xdfa print DFA as text ");

View File

@ -2,7 +2,6 @@ package org.antlr.v4.analysis;
import org.antlr.v4.automata.DFA; import org.antlr.v4.automata.DFA;
import org.antlr.v4.automata.DecisionState; import org.antlr.v4.automata.DecisionState;
import org.antlr.v4.tool.ErrorManager;
import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.Grammar;
public class AnalysisPipeline { public class AnalysisPipeline {
@ -27,7 +26,7 @@ public class AnalysisPipeline {
} }
public DFA createDFA(DecisionState s) { public DFA createDFA(DecisionState s) {
// TRY APPROXIMATE LL(*) ANALYSIS // TRY APPROXIMATE (STACK LIMITED) LL(*) ANALYSIS
StackLimitedNFAToDFAConverter conv = new StackLimitedNFAToDFAConverter(g, s); StackLimitedNFAToDFAConverter conv = new StackLimitedNFAToDFAConverter(g, s);
DFA dfa = conv.createDFA(); DFA dfa = conv.createDFA();
System.out.print("DFA="+dfa); System.out.print("DFA="+dfa);
@ -41,7 +40,7 @@ public class AnalysisPipeline {
System.out.println("MINIMIZE"); System.out.println("MINIMIZE");
DFAMinimizer dmin = new DFAMinimizer(dfa); DFAMinimizer dmin = new DFAMinimizer(dfa);
dmin.minimize(); dfa.minimized = dmin.minimize();
return dfa; return dfa;
} }
@ -50,21 +49,10 @@ public class AnalysisPipeline {
// limited version. Ambiguities are ok because if the approx version // limited version. Ambiguities are ok because if the approx version
// gets an ambiguity it's defin // gets an ambiguity it's defin
// REAL LL(*) ANALYSIS IF THAT FAILS // RECURSION LIMITED LL(*) ANALYSIS IF THAT FAILS
conv = new RecursionLimitedNFAToDFAConverter(g, s); conv = new RecursionLimitedNFAToDFAConverter(g, s);
try { dfa = conv.createDFA();
dfa = conv.createDFA(); System.out.print("DFA="+dfa);
System.out.print("DFA="+dfa);
}
catch (RecursionOverflowSignal ros) {
ErrorManager.recursionOverflow(g.fileName, dfa, ros.state, ros.altNum, ros.depth);
}
catch (MultipleRecursiveAltsSignal mras) {
ErrorManager.multipleRecursiveAlts(g.fileName, dfa, mras.recursiveAltSet);
}
catch (AnalysisTimeoutSignal at) {// TODO: nobody throws yet
ErrorManager.analysisTimeout();
}
conv.issueAmbiguityWarnings(); // ambig / unreachable errors conv.issueAmbiguityWarnings(); // ambig / unreachable errors
//conv.issueRecursionWarnings(); //conv.issueRecursionWarnings();
@ -72,13 +60,16 @@ public class AnalysisPipeline {
System.out.println("non-LL(*)"); System.out.println("non-LL(*)");
System.out.println("recursion limited NOT valid"); System.out.println("recursion limited NOT valid");
} }
else System.out.println("recursion limited valid"); else {
System.out.println("recursion limited valid");
System.out.println("MINIMIZE"); // gen DOT now to see non-minimized
DFAMinimizer dmin = new DFAMinimizer(dfa); System.out.println("DFA #states="+dfa.stateSet.size());
dmin.minimize(); //if ( g.tool.generate_NFA_dot ) g.tool.generateDFA(g, dfa);
System.out.println("MINIMIZE");
DFAMinimizer dmin = new DFAMinimizer(dfa);
dfa.minimized = dmin.minimize();
if ( dfa.minimized ) System.out.println("DFA minimized to #states="+dfa.stateSet.size());
}
return dfa; return dfa;
} }
} }

View File

@ -23,7 +23,7 @@ public class DFAMinimizer {
this.dfa = dfa; this.dfa = dfa;
} }
public void minimize() { public boolean minimize() {
int n = dfa.states.size(); int n = dfa.states.size();
boolean[][] distinct = new boolean[n][n]; boolean[][] distinct = new boolean[n][n];
@ -156,7 +156,7 @@ public class DFAMinimizer {
System.out.println("uniq sets = "+uniq); System.out.println("uniq sets = "+uniq);
if ( uniq.size()==dfa.states.size() ) { if ( uniq.size()==dfa.states.size() ) {
System.out.println("was already minimal"); System.out.println("was already minimal");
return; return false;
} }
// minimize the DFA (combine equiv sets) // minimize the DFA (combine equiv sets)
@ -207,6 +207,7 @@ public class DFAMinimizer {
} }
} }
} }
return true;
} }
void print(boolean[][] distinct) { void print(boolean[][] distinct) {

View File

@ -1,165 +0,0 @@
package org.antlr.v4.analysis;
import org.antlr.v4.automata.DFA;
import org.antlr.v4.automata.DFAState;
import org.antlr.v4.automata.Edge;
import org.antlr.v4.misc.Utils;
import org.stringtemplate.v4.misc.MultiMap;
import java.util.*;
/** Detect imperfect DFA:
*
* 1. nonreduced DFA (dangling states)
* 2. unreachable stop states
* 3. nondeterministic states
*
* TODO: unneeded?
*/
public class DFAVerifier {
public static enum ReachableStatus {
UNKNOWN,
BUSY, // in process of computing
NO,
YES;
}
Map<DFAState, ReachableStatus> status = new HashMap<DFAState, ReachableStatus>();
DFA dfa;
StackLimitedNFAToDFAConverter converter;
// create 2D matrix showing incident edges; inverse of adjacency matrix
// incidentEdges.get(s) is list of edges pointing at state s
MultiMap<DFAState, DFAState> incidentStates = new MultiMap<DFAState, DFAState>();
public DFAVerifier(DFA dfa, StackLimitedNFAToDFAConverter converter) {
this.dfa = dfa;
this.converter = converter;
for (DFAState d : dfa.stateSet.values()) {
for (Edge e : d.edges) incidentStates.map(e.target, d);
}
}
public Set<Integer> getUnreachableAlts() {
Set<Integer> unreachable = new HashSet<Integer>();
for (int alt=0; alt<=dfa.nAlts; alt++) {
if ( dfa.altToAcceptStates[alt]==null ) unreachable.add(alt);
}
return unreachable;
}
public List<DFAState> getIncidentEdgeStates(DFAState d) {
return incidentStates.get(d);
}
public Set<DFAState> getDeadStates() {
Set<DFAState> dead = new HashSet<DFAState>(dfa.stateSet.size());
dead.addAll(dfa.stateSet.values());
// for (DFAState a : dfa.altToAcceptState) {
// if ( a!=null ) dead.remove(a);
// }
// obviously accept states reach accept states
//reaches.addAll(Arrays.asList(dfa.altToAcceptState));
boolean changed = true;
while ( changed ) {
changed = false;
for (DFAState d : dfa.stateSet.values()) {
if ( !dead.contains(d) ) {
// if d isn't dead, it reaches accept state.
dead.remove(d);
// and, so do all states pointing at it
List<DFAState> incoming = incidentStates.get(d);
if ( incoming!=null ) dead.removeAll(incoming);
changed = true;
}
}
}
// boolean changed = true;
// while ( changed ) {
// changed = false;
// for (DFAState d : dfa.uniqueStates.values()) {
// if ( reaches.contains(d) ) {
// dead.remove(d);
// // if d reaches, so do all states pointing at it
// for (DFAState i : incidentStates.get(d)) {
// if ( !reaches.contains(i) ) {
// reaches.add(i);
// changed = true;
// }
// }
// }
// }
// }
System.out.println("dead="+dead);
return dead;
}
/** figure out if this state eventually reaches an accept state and
* modify the instance variable 'reduced' to indicate if we find
* at least one state that cannot reach an accept state. This implies
* that the overall DFA is not reduced. This algorithm should be
* linear in the number of DFA states.
*
* The algorithm also tracks which alternatives have no accept state,
* indicating a nondeterminism.
*
* Also computes whether the DFA is cyclic.
*
* TODO: I call getUniquelyPredicatedAlt too much; cache predicted alt
* TODO: convert to nonrecursive version.
*/
boolean _verify(DFAState d) { // TODO: rename to verify?
if ( d.isAcceptState ) {
// accept states have no edges emanating from them so we can return
status.put(d, ReachableStatus.YES);
// this alt is uniquely predicted, remove from nondeterministic list
int predicts = d.getUniquelyPredictedAlt();
converter.unreachableAlts.remove(Utils.integer(predicts));
return true;
}
// avoid infinite loops
status.put(d, ReachableStatus.BUSY);
boolean anEdgeReachesAcceptState = false;
// Visit every transition, track if at least one edge reaches stop state
// Cannot terminate when we know this state reaches stop state since
// all transitions must be traversed to set status of each DFA state.
for (int i=0; i<d.getNumberOfEdges(); i++) {
Edge t = d.edge(i);
DFAState edgeTarget = (DFAState)t.target;
ReachableStatus targetStatus = status.get(edgeTarget);
if ( targetStatus==ReachableStatus.BUSY ) { // avoid cycles; they say nothing
converter.cyclic = true;
continue;
}
if ( targetStatus==ReachableStatus.YES ) { // avoid unnecessary work
anEdgeReachesAcceptState = true;
continue;
}
if ( targetStatus==ReachableStatus.NO ) { // avoid unnecessary work
continue;
}
// target must be ReachableStatus.UNKNOWN (i.e., unvisited)
if ( _verify(edgeTarget) ) {
anEdgeReachesAcceptState = true;
// have to keep looking so don't break loop
// must cover all states even if we find a path for this state
}
}
if ( anEdgeReachesAcceptState ) {
status.put(d, ReachableStatus.YES);
}
else {
status.put(d, ReachableStatus.NO);
converter.reduced = false;
}
return anEdgeReachesAcceptState;
}
}

View File

@ -49,6 +49,7 @@ public class MachineProbe {
public List<IntSet> getEdgeLabels(DFAState targetState) { public List<IntSet> getEdgeLabels(DFAState targetState) {
List<DFAState> dfaStates = getAnyDFAPathToTarget(targetState); List<DFAState> dfaStates = getAnyDFAPathToTarget(targetState);
List<IntSet> labels = new ArrayList<IntSet>(); List<IntSet> labels = new ArrayList<IntSet>();
if ( dfaStates==null ) return labels;
for (int i=0; i<dfaStates.size()-1; i++) { for (int i=0; i<dfaStates.size()-1; i++) {
DFAState d = dfaStates.get(i); DFAState d = dfaStates.get(i);
DFAState nextState = dfaStates.get(i + 1); DFAState nextState = dfaStates.get(i + 1);

View File

@ -1,6 +1,9 @@
package org.antlr.v4.analysis; package org.antlr.v4.analysis;
import org.antlr.v4.automata.*; import org.antlr.v4.automata.DecisionState;
import org.antlr.v4.automata.NFAState;
import org.antlr.v4.automata.RuleTransition;
import org.antlr.v4.automata.Transition;
import org.antlr.v4.misc.BitSet; import org.antlr.v4.misc.BitSet;
import org.antlr.v4.misc.IntSet; import org.antlr.v4.misc.IntSet;
import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.Grammar;
@ -146,11 +149,6 @@ public class RecursionLimitedNFAToDFAConverter extends StackLimitedNFAToDFAConve
super(g, nfaStartState); super(g, nfaStartState);
} }
@Override
void reach(DFAState d) {
super.reach(d);
}
/** /**
* 1. Reach an NFA state associated with the end of a rule, r, in the * 1. Reach an NFA state associated with the end of a rule, r, in the
* grammar from which it was built. We must add an implicit (i.e., * grammar from which it was built. We must add an implicit (i.e.,

View File

@ -3,9 +3,9 @@ package org.antlr.v4.analysis;
import org.antlr.v4.automata.*; import org.antlr.v4.automata.*;
import org.antlr.v4.misc.IntervalSet; import org.antlr.v4.misc.IntervalSet;
import org.antlr.v4.misc.OrderedHashSet; import org.antlr.v4.misc.OrderedHashSet;
import org.antlr.v4.tool.ErrorManager;
import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.Rule; import org.antlr.v4.tool.Rule;
import org.stringtemplate.v4.misc.MultiMap;
import java.util.*; import java.util.*;
@ -66,21 +66,16 @@ public class StackLimitedNFAToDFAConverter {
*/ */
public Map<DFAState, List<Integer>> statesWithIncompletelyCoveredAlts = new HashMap<DFAState, List<Integer>>(); public Map<DFAState, List<Integer>> statesWithIncompletelyCoveredAlts = new HashMap<DFAState, List<Integer>>();
public boolean hasPredicateBlockedByAction = false; public boolean hasPredicateBlockedByAction = false;
/** Recursion is limited to a particular depth. If that limit is exceeded /** Recursion is limited to a particular depth. Which state tripped it? */
* the proposed new NFA configuration is recorded for the associated DFA state. public DFAState recursionOverflowState;
*/
protected MultiMap<DFAState, NFAConfig> stateToRecursionOverflowConfigurationsMap =
new MultiMap<DFAState, NFAConfig>();
//public Set<DFAState> recursionOverflowStates = new HashSet<DFAState>(); /** Which state found multiple recursive alts? */
public DFAState abortedDueToMultipleRecursiveAltsAt;
/** Are there any loops in this DFA? Computed by DFAVerifier */ /** Are there any loops in this DFA? */
public boolean cyclic = false; // public boolean cyclic = false;
/** Is this DFA reduced? I.e., can all states lead to an accept state? */
public boolean reduced = true;
/** Used to prevent the closure operation from looping to itself and /** Used to prevent the closure operation from looping to itself and
* hence looping forever. Sensitive to the NFA state, the alt, and * hence looping forever. Sensitive to the NFA state, the alt, and
@ -145,7 +140,20 @@ public class StackLimitedNFAToDFAConverter {
// closure(t); // add any NFA states reachable via epsilon // closure(t); // add any NFA states reachable via epsilon
// } // }
closure(t); // add any NFA states reachable via epsilon try {
closure(t); // add any NFA states reachable via epsilon
}
catch (RecursionOverflowSignal ros) {
recursionOverflowState = d;
ErrorManager.recursionOverflow(g.fileName, d, ros.state, ros.altNum, ros.depth);
}
catch (MultipleRecursiveAltsSignal mras) {
abortedDueToMultipleRecursiveAltsAt = d;
ErrorManager.multipleRecursiveAlts(g.fileName, d, mras.recursiveAltSet);
}
catch (AnalysisTimeoutSignal at) {// TODO: nobody throws yet
ErrorManager.analysisTimeout();
}
addTransition(d, label, t); // make d-label->t transition addTransition(d, label, t); // make d-label->t transition
} }
@ -218,7 +226,7 @@ public class StackLimitedNFAToDFAConverter {
// if we couldn't find any non-resolved edges to add, return nothing // if we couldn't find any non-resolved edges to add, return nothing
if ( labelTarget.nfaConfigs.size()==0 ) return null; if ( labelTarget.nfaConfigs.size()==0 ) return null;
return labelTarget; return labelTarget;
} }
@ -259,9 +267,9 @@ public class StackLimitedNFAToDFAConverter {
// off maybe by actions later hence we need a parameter to carry // off maybe by actions later hence we need a parameter to carry
// it forward // it forward
boolean collectPredicates = (d == dfa.startState); boolean collectPredicates = (d == dfa.startState);
closureBusy = new HashSet<NFAConfig>(); closureBusy = new HashSet<NFAConfig>();
List<NFAConfig> configs = new ArrayList<NFAConfig>(); List<NFAConfig> configs = new ArrayList<NFAConfig>();
for (NFAConfig c : d.nfaConfigs) { for (NFAConfig c : d.nfaConfigs) {
closure(c.state, c.alt, c.context, c.semanticContext, collectPredicates, configs); closure(c.state, c.alt, c.context, c.semanticContext, collectPredicates, configs);
@ -316,7 +324,7 @@ public class StackLimitedNFAToDFAConverter {
} }
// if we have context info and we're at rule stop state, do // if we have context info and we're at rule stop state, do
// local follow for invokingRule and global follow for other links // local follow for invokingRule and global follow for other links
void ruleStopStateClosure(NFAState s, int altNum, NFAContext context, void ruleStopStateClosure(NFAState s, int altNum, NFAContext context,
SemanticContext semanticContext, SemanticContext semanticContext,
boolean collectPredicates, boolean collectPredicates,
@ -407,7 +415,7 @@ public class StackLimitedNFAToDFAConverter {
} }
} }
} }
public OrderedHashSet<IntervalSet> getReachableLabels(DFAState d) { public OrderedHashSet<IntervalSet> getReachableLabels(DFAState d) {
OrderedHashSet<IntervalSet> reachableLabels = new OrderedHashSet<IntervalSet>(); OrderedHashSet<IntervalSet> reachableLabels = new OrderedHashSet<IntervalSet>();
for (NFAState s : d.getUniqueNFAStates()) { // for each state for (NFAState s : d.getUniqueNFAStates()) { // for each state

View File

@ -44,6 +44,9 @@ public class DFA {
/** We only want one accept state per predicted alt; track here */ /** We only want one accept state per predicted alt; track here */
public List<DFAState>[] altToAcceptStates; public List<DFAState>[] altToAcceptStates;
/** Did DFA minimization do anything? */
public boolean minimized;
/** Unique state numbers per DFA */ /** Unique state numbers per DFA */
int stateCounter = 0; int stateCounter = 0;
@ -91,7 +94,12 @@ public class DFA {
return converter.ambiguousStates.size()>0 && !resolvedWithPredicates; return converter.ambiguousStates.size()>0 && !resolvedWithPredicates;
} }
public boolean valid() { return converter.danglingStates.size()==0; } public boolean valid() {
return
converter.danglingStates.size()==0 &&
converter.abortedDueToMultipleRecursiveAltsAt ==null &&
converter.recursionOverflowState ==null;
}
public String toString() { public String toString() {
if ( startState==null ) return ""; if ( startState==null ) return "";

View File

@ -355,16 +355,16 @@ public class ErrorManager {
} }
public static void recursionOverflow(String fileName, public static void recursionOverflow(String fileName,
DFA dfa, NFAState s, int altNum, int depth) { DFAState d, NFAState s, int altNum, int depth) {
state.get().errors++; state.get().errors++;
Message msg = new RecursionOverflowMessage(fileName, dfa, s, altNum, depth); Message msg = new RecursionOverflowMessage(fileName, d, s, altNum, depth);
state.get().listener.error(msg); state.get().listener.error(msg);
} }
public static void multipleRecursiveAlts(String fileName, public static void multipleRecursiveAlts(String fileName,
DFA dfa, IntSet recursiveAltSet) { DFAState d, IntSet recursiveAltSet) {
state.get().errors++; state.get().errors++;
Message msg = new MultipleRecursiveAltsMessage(fileName, dfa, recursiveAltSet); Message msg = new MultipleRecursiveAltsMessage(fileName, d, recursiveAltSet);
state.get().listener.error(msg); state.get().listener.error(msg);
} }

View File

@ -1,18 +1,21 @@
package org.antlr.v4.tool; package org.antlr.v4.tool;
import org.antlr.v4.automata.DFA; import org.antlr.v4.automata.DFA;
import org.antlr.v4.automata.DFAState;
import org.antlr.v4.misc.IntSet; import org.antlr.v4.misc.IntSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class MultipleRecursiveAltsMessage extends Message { public class MultipleRecursiveAltsMessage extends Message {
public DFAState d;
public DFA dfa; public DFA dfa;
public IntSet recursiveAltSet; public IntSet recursiveAltSet;
public MultipleRecursiveAltsMessage(String fileName, DFA dfa, IntSet recursiveAltSet) { public MultipleRecursiveAltsMessage(String fileName, DFAState d, IntSet recursiveAltSet) {
super(ErrorType.MULTIPLE_RECURSIVE_ALTS); super(ErrorType.MULTIPLE_RECURSIVE_ALTS);
this.dfa = dfa; this.d = d;
this.dfa = d.dfa;
this.recursiveAltSet = recursiveAltSet; this.recursiveAltSet = recursiveAltSet;
this.line = dfa.decisionNFAStartState.ast.getLine(); this.line = dfa.decisionNFAStartState.ast.getLine();

View File

@ -1,19 +1,25 @@
package org.antlr.v4.tool; package org.antlr.v4.tool;
import org.antlr.v4.analysis.MachineProbe;
import org.antlr.v4.automata.DFA; import org.antlr.v4.automata.DFA;
import org.antlr.v4.automata.DFAState;
import org.antlr.v4.automata.NFAState; import org.antlr.v4.automata.NFAState;
import org.antlr.v4.misc.IntSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
public class RecursionOverflowMessage extends Message { public class RecursionOverflowMessage extends Message {
DFA dfa; DFA dfa;
DFAState d;
NFAState s; NFAState s;
int altNum; int altNum;
int depth; int depth;
public RecursionOverflowMessage(String fileName, DFA dfa, NFAState s, int altNum, int depth) { public RecursionOverflowMessage(String fileName, DFAState d, NFAState s, int altNum, int depth) {
super(ErrorType.RECURSION_OVERFLOW); super(ErrorType.RECURSION_OVERFLOW);
this.dfa = dfa; this.d = d;
this.dfa = d.dfa;
this.s = s; this.s = s;
this.altNum = altNum; this.altNum = altNum;
this.depth = depth; this.depth = depth;
@ -22,10 +28,16 @@ public class RecursionOverflowMessage extends Message {
this.charPosition = dfa.decisionNFAStartState.ast.getCharPositionInLine(); this.charPosition = dfa.decisionNFAStartState.ast.getCharPositionInLine();
this.fileName = fileName; this.fileName = fileName;
MachineProbe probe = new MachineProbe(dfa);
List<IntSet> labels = probe.getEdgeLabels(d);
String input = probe.getInputSequenceDisplay(dfa.g, labels);
Map<String, Object> info = new HashMap<String, Object>(); Map<String, Object> info = new HashMap<String, Object>();
info.put("dfa", dfa); info.put("dfa", dfa);
info.put("dfaState", d);
info.put("alt", altNum); info.put("alt", altNum);
info.put("depth", depth); info.put("depth", depth);
info.put("input", input);
info.put("nfaState", s); info.put("nfaState", s);
info.put("sourceRule", s.rule); info.put("sourceRule", s.rule);
info.put("targetRule", s.transition(0).target.rule); info.put("targetRule", s.transition(0).target.rule);