got more messages for recursion in there.

[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 6767]
This commit is contained in:
parrt 2010-03-22 17:29:49 -08:00
parent ad036f9ab1
commit a87f6e7666
15 changed files with 253 additions and 40 deletions

View File

@ -272,21 +272,29 @@ Input such as "<arg.input>" is insufficiently covered with predicates at locatio
<if(arg.hasPredicateBlockedByAction)><\n>At least one possibly relevant semantic predicate was hidden by action(s).<endif> <if(arg.hasPredicateBlockedByAction)><\n>At least one possibly relevant semantic predicate was hidden by action(s).<endif>
>> >>
DUPLICATE_SET_ENTRY(arg) ::= ANALYSIS_TIMEOUT(arg) ::= <<
"duplicate token type <arg> when collapsing subrule into set" 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
ANALYSIS_ABORTED(enclosingRule) ::= <<
ANTLR could not analyze this decision in rule <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(alt,input,targetRules,callSiteStates) ::= <<
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 "> 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 ">
>> >>
LEFT_RECURSION_CYCLES(arg) ::= <<
The following sets of rules are mutually left-recursive <arg:{c| [<c:{r|<r.name>}; separator=", ">]}; separator=" and ">
>>
MULTIPLE_RECURSIVE_ALTS(arg) ::= <<
[fatal] rule <arg.ruleName> has non-LL(*) decision due to recursive rule invocations reachable from alts <arg.alts; separator=",">. Resolve by left-factoring or using syntactic predicates or using backtrack=true option.
>>
UNREACHABLE_TOKENS(tokens) ::= << UNREACHABLE_TOKENS(tokens) ::= <<
The following token definitions can never be matched because prior tokens match the same input: <tokens; separator=","> The following token definitions can never be matched because prior tokens match the same input: <tokens; separator=",">
>> >>
DUPLICATE_SET_ENTRY(arg) ::=
"duplicate token type <arg> when collapsing subrule into set"
TOKEN_NONDETERMINISM(input,conflictingTokens,paths,disabled,hasPredicateBlockedByAction) ::= TOKEN_NONDETERMINISM(input,conflictingTokens,paths,disabled,hasPredicateBlockedByAction) ::=
<< <<
<if(paths)> <if(paths)>
@ -297,15 +305,6 @@ Multiple token rules can match input such as "<input>": <conflictingTokens; sepa
<endif> <endif>
<if(disabled)><\n>As a result, token(s) <disabled; separator=","> were disabled for that input<endif><if(hasPredicateBlockedByAction)><\n>Semantic predicates were present but were hidden by actions.<endif> <if(disabled)><\n>As a result, token(s) <disabled; separator=","> were disabled for that input<endif><if(hasPredicateBlockedByAction)><\n>Semantic predicates were present but were hidden by actions.<endif>
>> >>
LEFT_RECURSION_CYCLES(arg) ::= <<
The following sets of rules are mutually left-recursive <arg:{c| [<c:{r|<r.name>}; separator=", ">]}; separator=" and ">
>>
NONREGULAR_DECISION(ruleName,alts) ::= <<
[fatal] rule <ruleName> has non-LL(*) decision due to recursive rule invocations reachable from alts <alts; separator=",">. Resolve by left-factoring or using syntactic predicates or using backtrack=true option.
>>
/* l10n for message levels */ /* l10n for message levels */
WARNING() ::= "warning" WARNING() ::= "warning"
ERROR() ::= "error" ERROR() ::= "error"

View File

@ -2,6 +2,7 @@ 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 {
@ -36,8 +37,7 @@ public class AnalysisPipeline {
if ( dfa.valid() ) System.out.println("stack limited valid"); if ( dfa.valid() ) System.out.println("stack limited valid");
if ( dfa.valid() ) { if ( dfa.valid() ) {
// ambig / unreachable errors conv.issueAmbiguityWarnings(); // ambig / unreachable errors
conv.issueAmbiguityWarnings();
System.out.println("MINIMIZE"); System.out.println("MINIMIZE");
DFAMinimizer dmin = new DFAMinimizer(dfa); DFAMinimizer dmin = new DFAMinimizer(dfa);
@ -52,14 +52,25 @@ public class AnalysisPipeline {
// REAL LL(*) ANALYSIS IF THAT FAILS // REAL LL(*) ANALYSIS IF THAT FAILS
conv = new RecursionLimitedNFAToDFAConverter(g, s); conv = new RecursionLimitedNFAToDFAConverter(g, s);
dfa = conv.createDFA(); try {
System.out.print("DFA="+dfa); dfa = conv.createDFA();
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();
}
// ambig / unreachable errors conv.issueAmbiguityWarnings(); // ambig / unreachable errors
conv.issueAmbiguityWarnings(); //conv.issueRecursionWarnings();
if ( !dfa.valid() ) { if ( !dfa.valid() ) {
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");

View File

@ -0,0 +1,7 @@
package org.antlr.v4.analysis;
public class AnalysisTimeoutSignal extends RuntimeException {
public AnalysisTimeoutSignal() {
}
}

View File

@ -34,7 +34,7 @@ public class LeftRecursionDetector {
} }
//System.out.println("cycles="+listOfRecursiveCycles); //System.out.println("cycles="+listOfRecursiveCycles);
if ( listOfRecursiveCycles.size()>0 ) { if ( listOfRecursiveCycles.size()>0 ) {
ErrorManager.leftRecursionCycles(listOfRecursiveCycles); ErrorManager.leftRecursionCycles(nfa.g.fileName, listOfRecursiveCycles);
} }
} }

View File

@ -0,0 +1,10 @@
package org.antlr.v4.analysis;
import org.antlr.v4.misc.IntSet;
public class MultipleRecursiveAltsSignal extends RuntimeException {
public IntSet recursiveAltSet;
public MultipleRecursiveAltsSignal(IntSet recursiveAltSet) {
this.recursiveAltSet = recursiveAltSet;
}
}

View File

@ -215,14 +215,14 @@ public class RecursionLimitedNFAToDFAConverter extends StackLimitedNFAToDFAConve
if ( depth==1 ) { // recursion if ( depth==1 ) { // recursion
recursiveAltSet.add(altNum); // indicate that this alt is recursive recursiveAltSet.add(altNum); // indicate that this alt is recursive
if ( recursiveAltSet.size()>1 ) { if ( recursiveAltSet.size()>1 ) {
throw new RuntimeException("recursion in >1 alt: "+recursiveAltSet); throw new MultipleRecursiveAltsSignal(recursiveAltSet);
} }
} }
// Detect an attempt to recurse too high // Detect an attempt to recurse too high
// if this context has hit the max recursions for p.stateNumber, // if this context has hit the max recursions for p.stateNumber,
// don't allow it to enter p.stateNumber again // don't allow it to enter p.stateNumber again
if ( depth >= r ) { if ( depth >= r ) {
throw new RuntimeException("recursion overflow in "+altNum); throw new RecursionOverflowSignal(altNum, depth, s);
} }
// first create a new context and push onto call tree, // first create a new context and push onto call tree,
// recording the fact that we are invoking a rule and // recording the fact that we are invoking a rule and

View File

@ -0,0 +1,14 @@
package org.antlr.v4.analysis;
import org.antlr.v4.automata.NFAState;
public class RecursionOverflowSignal extends RuntimeException {
int altNum;
int depth;
NFAState state;
public RecursionOverflowSignal(int altNum, int depth, NFAState state) {
this.altNum = altNum;
this.depth = depth;
this.state = state;
}
}

View File

@ -245,9 +245,7 @@ public class Resolver {
return alt; return alt;
} }
public void issueAmbiguityWarnings() {
void issueAmbiguityWarnings() {
MachineProbe probe = new MachineProbe(converter.dfa); MachineProbe probe = new MachineProbe(converter.dfa);
for (DFAState d : converter.ambiguousStates) { for (DFAState d : converter.ambiguousStates) {
@ -295,9 +293,89 @@ public class Resolver {
converter.hasPredicateBlockedByAction); converter.hasPredicateBlockedByAction);
} }
} }
if ( converter.unreachableAlts.size()>0 ) { if ( converter.unreachableAlts!=null && converter.unreachableAlts.size()>0 ) {
ErrorManager.unreachableAlts(converter.g.fileName, converter.dfa, converter.unreachableAlts); ErrorManager.unreachableAlts(converter.g.fileName, converter.dfa, converter.unreachableAlts);
} }
} }
/*
void issueRecursionWarnings() {
// RECURSION OVERFLOW
Set dfaStatesWithRecursionProblems =
converter.stateToRecursionOverflowConfigurationsMap.keySet();
// now walk truly unique (unaliased) list of dfa states with inf recur
// Goal: create a map from alt to map<target,List<callsites>>
// Map<Map<String target, List<NFAState call sites>>
Map<Integer, Map<>> altToT argetToCallSitesMap = new HashMap();
// track a single problem DFA state for each alt
Map<Integer, DFAState> altToDFAState = new HashMap<Integer, DFAState>();
computeAltToProblemMaps(dfaStatesWithRecursionProblems,
converter.stateToRecursionOverflowConfigurationsMap,
altToTargetToCallSitesMap, // output param
altToDFAState); // output param
// walk each alt with recursion overflow problems and generate error
Set<Integer> alts = altToTargetToCallSitesMap.keySet();
List<Integer> sortedAlts = new ArrayList<Integer>(alts);
Collections.sort(sortedAlts);
for (Iterator altsIt = sortedAlts.iterator(); altsIt.hasNext();) {
Integer altI = (Integer) altsIt.next();
Map<Integer, > targetToCallSiteMap =
altToTargetToCallSitesMap.get(altI);
Set targetRules = targetToCallSiteMap.keySet();
Collection callSiteStates = targetToCallSiteMap.values();
DFAState sampleBadState = altToDFAState.get(altI);
ErrorManager.recursionOverflow(this,
sampleBadState,
altI.intValue(),
targetRules,
callSiteStates);
}
}
void computeAltToProblemMaps(Set<DFAState> dfaStatesUnaliased,
Map configurationsMap,
Map<Integer, NFAState> altToTargetToCallSitesMap,
Map altToDFAState)
{
for (DFAState d : dfaStatesUnaliased) {
for (NFAConfig c : d.nfaConfigs) {
NFAState ruleInvocationState = c.state;
RuleTransition rt = (RuleTransition)ruleInvocationState.transition(0);
String targetRule = rt.rule.name;
}
}
for (Iterator it = dfaStatesUnaliased.iterator(); it.hasNext();) {
Integer stateI = (Integer) it.next();
// walk this DFA's config list
List configs = (List)configurationsMap.get(stateI);
for (int i = 0; i < configs.size(); i++) {
NFAConfig c = (NFAConfig) configs.get(i);
NFAState ruleInvocationState = c.state;
Transition transition0 = ruleInvocationState.transition(0);
RuleTransition ref = (RuleTransition)transition0;
String targetRule = ((NFAState) ref.target).rule.name;
Integer altI = org.antlr.misc.Utils.integer(c.alt);
Map<Integer, NFAState> targetToCallSiteMap =
altToTargetToCallSitesMap.get(altI);
if ( targetToCallSiteMap==null ) {
targetToCallSiteMap = new HashMap();
altToTargetToCallSitesMap.put(altI, targetToCallSiteMap);
}
Set<NFAState> callSites = targetToCallSiteMap.get(targetRule);
if ( callSites==null ) {
callSites = new HashSet();
targetToCallSiteMap.put(targetRule, callSites);
}
callSites.add(ruleInvocationState);
// track one problem DFA state per alt
if ( altToDFAState.get(altI)==null ) {
DFAState sampleBadState = converter.dfa.states.get(stateI.intValue());
altToDFAState.put(altI, sampleBadState);
}
}
}
}
*/
} }

View File

@ -5,6 +5,7 @@ import org.antlr.v4.misc.IntervalSet;
import org.antlr.v4.misc.OrderedHashSet; import org.antlr.v4.misc.OrderedHashSet;
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.*;
@ -67,7 +68,13 @@ public class StackLimitedNFAToDFAConverter {
public boolean hasPredicateBlockedByAction = false; public boolean hasPredicateBlockedByAction = false;
public Set<DFAState> recursionOverflowStates = new HashSet<DFAState>(); /** Recursion is limited to a particular depth. If that limit is exceeded
* the proposed new NFA configuration is recorded for the associated DFA state.
*/
protected MultiMap<DFAState, NFAConfig> stateToRecursionOverflowConfigurationsMap =
new MultiMap<DFAState, NFAConfig>();
//public Set<DFAState> recursionOverflowStates = new HashSet<DFAState>();
/** Are there any loops in this DFA? Computed by DFAVerifier */ /** Are there any loops in this DFA? Computed by DFAVerifier */
public boolean cyclic = false; public boolean cyclic = false;
@ -575,4 +582,5 @@ public class StackLimitedNFAToDFAConverter {
} }
void issueAmbiguityWarnings() { resolver.issueAmbiguityWarnings(); } void issueAmbiguityWarnings() { resolver.issueAmbiguityWarnings(); }
//void issueRecursionWarnings() { resolver.issueRecursionWarnings(); }
} }

View File

@ -0,0 +1,7 @@
package org.antlr.v4.tool;
public class AnalysisTimeoutMessage extends Message {
public AnalysisTimeoutMessage() {
super(ErrorType.ANALYSIS_TIMEOUT);
}
}

View File

@ -6,6 +6,8 @@ import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token; import org.antlr.runtime.Token;
import org.antlr.v4.automata.DFA; import org.antlr.v4.automata.DFA;
import org.antlr.v4.automata.DFAState; import org.antlr.v4.automata.DFAState;
import org.antlr.v4.automata.NFAState;
import org.antlr.v4.misc.IntSet;
import org.antlr.v4.parse.v4ParserException; import org.antlr.v4.parse.v4ParserException;
import org.stringtemplate.v4.ST; import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STErrorListener; import org.stringtemplate.v4.STErrorListener;
@ -346,17 +348,31 @@ public class ErrorManager {
state.get().listener.warning(msg); state.get().listener.warning(msg);
} }
public static void leftRecursionCycles(Collection cycles) { public static void leftRecursionCycles(String fileName, Collection cycles) {
state.get().errors++; state.get().errors++;
Message msg = new LeftRecursionCyclesMessage(cycles); Message msg = new LeftRecursionCyclesMessage(fileName, cycles);
state.get().listener.warning(msg); state.get().listener.error(msg);
} }
/** Process a new message by sending it on to the error listener associated with the current thread public static void recursionOverflow(String fileName,
* and recording any information we need in the error state for the current thread. DFA dfa, NFAState s, int altNum, int depth) {
*/ state.get().errors++;
private static void processMessage() { Message msg = new RecursionOverflowMessage(fileName, dfa, s, altNum, depth);
} state.get().listener.error(msg);
}
public static void multipleRecursiveAlts(String fileName,
DFA dfa, IntSet recursiveAltSet) {
state.get().errors++;
Message msg = new MultipleRecursiveAltsMessage(fileName, dfa, recursiveAltSet);
state.get().listener.error(msg);
}
public static void analysisTimeout() {
state.get().errors++;
Message msg = new AnalysisTimeoutMessage();
state.get().listener.error(msg);
}
public static int getNumErrors() { public static int getNumErrors() {
return state.get().errors; return state.get().errors;

View File

@ -134,10 +134,14 @@ public enum ErrorType {
AMBIGUITY(ErrorSeverity.ERROR, true, true), AMBIGUITY(ErrorSeverity.ERROR, true, true),
UNREACHABLE_ALTS(ErrorSeverity.ERROR, true, true), UNREACHABLE_ALTS(ErrorSeverity.ERROR, true, true),
RECURSION_OVERFLOW(ErrorSeverity.ERROR, true, true), MULTIPLE_RECURSIVE_ALTS(ErrorSeverity.ERROR, true, true),
INSUFFICIENT_PREDICATES(ErrorSeverity.ERROR, true, true), INSUFFICIENT_PREDICATES(ErrorSeverity.ERROR, true, true),
// these next 3 can happen in recursion-limited LL(*)
RECURSION_OVERFLOW(ErrorSeverity.ERROR, true, true),
LEFT_RECURSION_CYCLES(ErrorSeverity.ERROR, true, true), LEFT_RECURSION_CYCLES(ErrorSeverity.ERROR, true, true),
ANALYSIS_TIMEOUT(ErrorSeverity.ERROR, true, true),
/** Documentation comment is unterminated */ /** Documentation comment is unterminated */
//UNTERMINATED_DOC_COMMENT(ErrorSeverity.ERROR, true, true), //UNTERMINATED_DOC_COMMENT(ErrorSeverity.ERROR, true, true),

View File

@ -5,8 +5,9 @@ import java.util.Collection;
public class LeftRecursionCyclesMessage extends Message { public class LeftRecursionCyclesMessage extends Message {
public Collection cycles; public Collection cycles;
public LeftRecursionCyclesMessage(Collection cycles) { public LeftRecursionCyclesMessage(String fileName, Collection cycles) {
super(ErrorType.LEFT_RECURSION_CYCLES, cycles); super(ErrorType.LEFT_RECURSION_CYCLES, cycles);
this.cycles = cycles; this.cycles = cycles;
this.fileName = fileName;
} }
} }

View File

@ -0,0 +1,28 @@
package org.antlr.v4.tool;
import org.antlr.v4.automata.DFA;
import org.antlr.v4.misc.IntSet;
import java.util.HashMap;
import java.util.Map;
public class MultipleRecursiveAltsMessage extends Message {
public DFA dfa;
public IntSet recursiveAltSet;
public MultipleRecursiveAltsMessage(String fileName, DFA dfa, IntSet recursiveAltSet) {
super(ErrorType.MULTIPLE_RECURSIVE_ALTS);
this.dfa = dfa;
this.recursiveAltSet = recursiveAltSet;
this.line = dfa.decisionNFAStartState.ast.getLine();
this.charPosition = dfa.decisionNFAStartState.ast.getCharPositionInLine();
this.fileName = fileName;
Map<String, Object> info = new HashMap<String, Object>();
info.put("dfa", dfa);
info.put("ruleName", dfa.decisionNFAStartState.rule.name);
info.put("alts", recursiveAltSet);
args = new Object[] {info}; // pass this whole object in to message
}
}

View File

@ -1,4 +1,34 @@
package org.antlr.v4.tool; package org.antlr.v4.tool;
import org.antlr.v4.automata.DFA;
import org.antlr.v4.automata.NFAState;
import java.util.HashMap;
import java.util.Map;
public class RecursionOverflowMessage extends Message { public class RecursionOverflowMessage extends Message {
DFA dfa;
NFAState s;
int altNum;
int depth;
public RecursionOverflowMessage(String fileName, DFA dfa, NFAState s, int altNum, int depth) {
super(ErrorType.RECURSION_OVERFLOW);
this.dfa = dfa;
this.s = s;
this.altNum = altNum;
this.depth = depth;
this.line = dfa.decisionNFAStartState.ast.getLine();
this.charPosition = dfa.decisionNFAStartState.ast.getCharPositionInLine();
this.fileName = fileName;
Map<String, Object> info = new HashMap<String, Object>();
info.put("dfa", dfa);
info.put("alt", altNum);
info.put("depth", depth);
info.put("nfaState", s);
info.put("sourceRule", s.rule);
info.put("targetRule", s.transition(0).target.rule);
args = new Object[] {info}; // pass this whole object in to message
}
} }