Skip to content

Commit

Permalink
Evaluate predicates for all ambiguous alternatives and report if stil…
Browse files Browse the repository at this point in the history
…l ambiguous after predicate evaluation. Remove misleading message insufficient predicates. Fixes #31. Partially addresses #39.
  • Loading branch information
sharwell committed Mar 10, 2012
1 parent 7525bb6 commit 2cc39c2
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 126 deletions.
12 changes: 0 additions & 12 deletions runtime/Java/src/org/antlr/v4/runtime/ANTLRErrorStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,4 @@ void reportContextSensitivity(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex,
@NotNull ATNConfigSet configs);

/** Called by the parser when it finds less than n-1 predicates for n ambiguous alternatives.
* If there are n-1, we assume that the missing predicate is !(the "or" of the other predicates).
* If there are fewer than n-1, then we don't know which make it alternative to protect
* if the predicates fail.
*/
void reportInsufficientPredicates(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex, @NotNull IntervalSet ambigAlts,
DecisionState decState,
@NotNull SemanticContext[] altToPred,
@NotNull ATNConfigSet configs, boolean fullContextParse);
}
11 changes: 0 additions & 11 deletions runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -570,15 +570,4 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df
int startIndex, int stopIndex, @NotNull ATNConfigSet configs)
{
}

@Override
public void reportInsufficientPredicates(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex,
@NotNull IntervalSet ambigAlts,
DecisionState decState,
@NotNull SemanticContext[] altToPred,
@NotNull ATNConfigSet configs, boolean fullContextParse)
{
}
}
18 changes: 0 additions & 18 deletions runtime/Java/src/org/antlr/v4/runtime/DiagnosticErrorStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,10 @@ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
package org.antlr.v4.runtime;

import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.atn.SemanticContext;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.NotNull;

import java.util.Arrays;

public class DiagnosticErrorStrategy extends DefaultErrorStrategy {
@Override
public void reportAmbiguity(@NotNull Parser recognizer,
Expand Down Expand Up @@ -65,18 +61,4 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df
recognizer.notifyErrorListeners("reportContextSensitivity d=" + dfa.decision + ": " + configs + ", input='" +
recognizer.getInputString(startIndex, stopIndex) + "'");
}

@Override
public void reportInsufficientPredicates(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex,
@NotNull IntervalSet ambigAlts,
DecisionState decState,
@NotNull SemanticContext[] altToPred,
@NotNull ATNConfigSet configs, boolean fullContextParse)
{
recognizer.notifyErrorListeners("reportInsufficientPredicates d=" + dfa.decision + ", decState=" + decState +
", ambigAlts=" + ambigAlts + ":" + Arrays.toString(altToPred) +
", " + configs + ", input='" + recognizer.getInputString(startIndex, stopIndex) + "'");
}
}
143 changes: 61 additions & 82 deletions runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,11 +417,12 @@ public int execDFA(@NotNull DFA dfa, @NotNull DFAState s0,
if ( s.predicates!=null ) {
// rewind input so pred's LT(i) calls make sense
input.seek(startIndex);
int predictedAlt = evalSemanticContext(s.predicates, outerContext);
if ( predictedAlt!=ATN.INVALID_ALT_NUMBER ) {
return predictedAlt;
IntervalSet alts = evalSemanticContext(s.predicates, outerContext);
if (alts.isNil()) {
throw noViableAlt(input, outerContext, s.configset, startIndex);
}
throw noViableAlt(input, outerContext, s.configset, startIndex);

return alts.getMinElement();
}

if ( dfa_debug ) System.out.println("DFA decision "+dfa.decision+
Expand Down Expand Up @@ -566,26 +567,23 @@ public int execATN(@NotNull DFA dfa, @NotNull DFAState s0,
List<DFAState.PredPrediction> predPredictions =
predicateDFAState(D, D.configset, outerContext, nalts);
if ( predPredictions!=null ) {
IntervalSet conflictingAlts = getConflictingAltsFromConfigSet(D.configset);
if ( D.predicates.size() < conflictingAlts.size() ) {
reportInsufficientPredicates(dfa, startIndex, input.index(),
conflictingAlts,
decState,
getPredsForAmbigAlts(conflictingAlts, D.configset, nalts),
D.configset,
false);
}
int stopIndex = input.index();
input.seek(startIndex);
predictedAlt = evalSemanticContext(predPredictions, outerContext);
if ( predictedAlt!=ATN.INVALID_ALT_NUMBER ) {
return predictedAlt;
IntervalSet alts = evalSemanticContext(predPredictions, outerContext);
D.prediction = ATN.INVALID_ALT_NUMBER;
switch (alts.size()) {
case 0:
throw noViableAlt(input, outerContext, D.configset, startIndex);

case 1:
return alts.getMinElement();

default:
// report ambiguity after predicate evaluation to make sure the correct
// set of ambig alts is reported.
reportAmbiguity(dfa, D, startIndex, stopIndex, alts, D.configset);
return alts.getMinElement();
}

// Consistency check - the DFAState should not have a "fallback"
// prediction specified for the case where no predicates succeed.
assert D.prediction == ATN.INVALID_ALT_NUMBER;

throw noViableAlt(input, outerContext, D.configset, startIndex);
}
}

Expand Down Expand Up @@ -642,24 +640,26 @@ public ATNConfigSet execATNWithFullContext(DFA dfa,
if ( altToPred!=null ) {
// we have a validating predicate; test it
predPredictions = getPredicatePredictions(reach.conflictingAlts, altToPred);
if ( predPredictions.size() < nalts ) {
IntervalSet conflictingAlts = getConflictingAltsFromConfigSet(reach);
DecisionState decState = atn.getDecisionState(dfa.decision);
reportInsufficientPredicates(dfa, startIndex, input.index(),
conflictingAlts,
decState, altToPred,
reach, true);
}
input.seek(startIndex);
reach.uniqueAlt = evalSemanticContext(predPredictions, outerContext);
if ( reach.uniqueAlt != ATN.INVALID_ALT_NUMBER ) {
IntervalSet alts = evalSemanticContext(predPredictions, outerContext);
reach.uniqueAlt = ATN.INVALID_ALT_NUMBER;
switch (alts.size()) {
case 0:
throw noViableAlt(input, outerContext, reach, startIndex);

case 1:
reach.uniqueAlt = alts.getMinElement();
return reach;

default:
// reach.conflictingAlts holds the post-evaluation set of ambig alts
reach.conflictingAlts = alts;
break;
}
throw noViableAlt(input, outerContext, reach, startIndex);
}
}

// must have conflict and no semantic preds
// must have conflict
reportAmbiguity(dfa, D, startIndex, input.index(), reach.conflictingAlts, reach);
reach.uniqueAlt = reach.conflictingAlts.getMinElement();

Expand Down Expand Up @@ -786,7 +786,7 @@ else if (altToPred[i] != SemanticContext.NONE) {

// Optimize away p||p and p&&p
for (int i = 0; i < altToPred.length; i++) {
if ( altToPred[i]!=null ) altToPred[i] = altToPred[i].optimize();
altToPred[i] = altToPred[i].optimize();
}

// nonambig alts are null in altToPred
Expand All @@ -797,64 +797,62 @@ else if (altToPred[i] != SemanticContext.NONE) {

public List<DFAState.PredPrediction> getPredicatePredictions(IntervalSet ambigAlts, SemanticContext[] altToPred) {
List<DFAState.PredPrediction> pairs = new ArrayList<DFAState.PredPrediction>();
int firstUnpredicated = ATN.INVALID_ALT_NUMBER;
// keep track of the position in pairs where the unpredicated choice should go
int firstUnpredicatedIndex = -1;
boolean containsPredicate = false;
for (int i = 1; i < altToPred.length; i++) {
SemanticContext pred = altToPred[i];

// unpredicated is indicated by SemanticContext.NONE
assert pred != null;

// find first unpredicated but ambig alternative, if any.
// Only ambiguous alternatives will have SemanticContext.NONE.
// Any unambig alts or ambig naked alts after first ambig naked are ignored
// (null, i) means alt i is the default prediction
// if no (null, i), then no default prediction.
if ( ambigAlts!=null && ambigAlts.contains(i) &&
pred==SemanticContext.NONE && firstUnpredicated==ATN.INVALID_ALT_NUMBER )
{
firstUnpredicated = i;
firstUnpredicatedIndex = pairs.size();
if (ambigAlts!=null && ambigAlts.contains(i) && pred==SemanticContext.NONE) {
pairs.add(new DFAState.PredPrediction(null, i));
}
if ( pred!=null && pred!=SemanticContext.NONE ) {
else if ( pred!=SemanticContext.NONE ) {
containsPredicate = true;
pairs.add(new DFAState.PredPrediction(pred, i));
}
}
if ( pairs.isEmpty() ) pairs = null;
else if ( firstUnpredicated!=ATN.INVALID_ALT_NUMBER ) {
// add default prediction if we found null predicate
pairs.add(firstUnpredicatedIndex, new DFAState.PredPrediction(null, firstUnpredicated));

if ( !containsPredicate ) {
pairs = null;
}

// System.out.println(Arrays.toString(altToPred)+"->"+pairs);
return pairs;
}

/** Look through a list of predicate/alt pairs, returning alt for the
* first pair that wins. A null predicate indicates the default
* prediction for disambiguating predicates.
/** Look through a list of predicate/alt pairs, returning alts for the
* pairs that win. A {@code null} predicate indicates an alt containing an
* unpredicated config which behaves as "always true."
*/
public int evalSemanticContext(List<DFAState.PredPrediction> predPredictions,
ParserRuleContext<?> outerContext)
public IntervalSet evalSemanticContext(List<DFAState.PredPrediction> predPredictions,
ParserRuleContext<?> outerContext)
{
int predictedAlt = ATN.INVALID_ALT_NUMBER;
// List<DFAState.PredPrediction> predPredictions = D.predicates;
// if ( D.isCtxSensitive ) predPredictions = D.ctxToPredicates.get(outerContext);
IntervalSet predictions = new IntervalSet();
for (DFAState.PredPrediction pair : predPredictions) {
if ( pair.pred==null ) {
predictedAlt = pair.alt; // default prediction
break;
predictions.add(pair.alt);
continue;
}

boolean evaluatedResult = pair.pred.eval(parser, outerContext);
if ( debug || dfa_debug ) {
System.out.println("eval pred "+pair+"="+evaluatedResult);
}

if ( evaluatedResult ) {
if ( debug || dfa_debug ) System.out.println("PREDICT "+pair.alt);
predictedAlt = pair.alt;
break;
predictions.add(pair.alt);
continue;
}
}
// if no prediction: either all predicates are false and
// all alternatives were guarded, or a validating predicate failed.
return predictedAlt;

return predictions;
}


Expand Down Expand Up @@ -1413,23 +1411,4 @@ public void reportAmbiguity(@NotNull DFA dfa, DFAState D, int startIndex, int st
if ( parser!=null ) parser.getErrorHandler().reportAmbiguity(parser, dfa, startIndex, stopIndex,
ambigAlts, configs);
}

public void reportInsufficientPredicates(@NotNull DFA dfa, int startIndex, int stopIndex,
@NotNull IntervalSet ambigAlts,
DecisionState decState,
@NotNull SemanticContext[] altToPred,
@NotNull ATNConfigSet configs,
boolean fullContextParse)
{
if ( debug || retry_debug ) {
System.out.println("reportInsufficientPredicates "+
ambigAlts+", decState="+decState+": "+Arrays.toString(altToPred)+
parser.getInputString(startIndex, stopIndex));
}
if ( parser!=null ) {
parser.getErrorHandler().reportInsufficientPredicates(parser, dfa, startIndex, stopIndex, ambigAlts,
decState, altToPred, configs, fullContextParse);
}
}

}
6 changes: 3 additions & 3 deletions tool/test/org/antlr/v4/test/TestSemPredEvalParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public class TestSemPredEvalParser extends BaseTest {
"alt 1\n" +
"alt 1\n";
assertEquals(expecting, found);
assertEquals("line 1:0 reportInsufficientPredicates d=0, decState=24, ambigAlts={1..3}:[{-1:-1}?, {-1:-1}?, {-1:-1}?, {1:0}?], [(6,1,[],up=1), (1,1,[],up=1), (6,2,[],up=1), (1,2,[],up=1), (6,3,[],{1:0}?,up=1), (1,3,[],{1:0}?,up=1)],hasSemanticContext=true,conflictingAlts={1..3},dipsIntoOuterContext, input='x'\n",
assertEquals("line 1:0 reportAmbiguity d=0: ambigAlts={1..2}:[(6,1,[],up=1), (1,1,[],up=1), (6,2,[],up=1), (1,2,[],up=1), (6,3,[],{1:0}?,up=1), (1,3,[],{1:0}?,up=1)],hasSemanticContext=true,conflictingAlts={1..3},dipsIntoOuterContext, input='x'\n",
this.stderrDuringParse);
}

Expand Down Expand Up @@ -184,8 +184,8 @@ public class TestSemPredEvalParser extends BaseTest {
"alt 2\n" +
"alt 2\n";
assertEquals(expecting, found);
assertEquals("line 1:4 reportInsufficientPredicates d=0, decState=32, ambigAlts={2..4}:[{-1:-1}?, {-1:-1}?, {-1:-1}?, {-1:-1}?, {1:0}?], [(6,2,[],up=1), (10,2,[],up=1), (1,2,[],up=1), (6,3,[],up=1), (10,3,[],up=1), (1,3,[],up=1), (6,4,[],{1:0}?,up=1), (10,4,[],{1:0}?,up=1), (1,4,[],{1:0}?,up=1)],hasSemanticContext=true,conflictingAlts={2..4},dipsIntoOuterContext, input='x'\n",
this.stderrDuringParse);
assertEquals("line 1:4 reportAmbiguity d=0: ambigAlts={2..3}:[(6,2,[],up=1), (10,2,[],up=1), (1,2,[],up=1), (6,3,[],up=1), (10,3,[],up=1), (1,3,[],up=1), (6,4,[],{1:0}?,up=1), (10,4,[],{1:0}?,up=1), (1,4,[],{1:0}?,up=1)],hasSemanticContext=true,conflictingAlts={2..4},dipsIntoOuterContext, input='x'\n",
this.stderrDuringParse);
}

@Test public void testRewindBeforePredEval() throws Exception {
Expand Down

0 comments on commit 2cc39c2

Please sign in to comment.