add new interpreter that knows how to track alternatives in rule nodes; some cleanup and improvement to other interpreter

This commit is contained in:
parrt 2015-06-17 18:06:22 -07:00
parent 4bee04f114
commit 94bef386cb
9 changed files with 401 additions and 143 deletions

View File

@ -32,7 +32,6 @@ package org.antlr.v4.runtime;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNDeserializationOptions;
import org.antlr.v4.runtime.atn.ATNDeserializer;
import org.antlr.v4.runtime.atn.ATNSerializer;
import org.antlr.v4.runtime.atn.ATNSimulator;
import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.atn.AmbiguityInfo;
@ -48,7 +47,6 @@ import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Trees;
import org.antlr.v4.runtime.tree.pattern.ParseTreePattern;
import org.antlr.v4.runtime.tree.pattern.ParseTreePatternMatcher;
@ -768,116 +766,6 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return false;
}
/** Given an AmbiguityInfo object that contains information about an
* ambiguous decision event, return the list of ambiguous parse trees.
* An ambiguity occurs when a specific token sequence can be recognized
* in more than one way by the grammar. These ambiguities are detected only
* at decision points.
*
* The list of trees includes the actual interpretation (that for
* the minimum alternative number) and all ambiguous alternatives.
* The actual interpretation is always first.
*
* This method reuses the same physical input token stream used to
* detect the ambiguity by the original parser in the first place.
* This method resets/seeks within but does not alter originalParser.
* The input position is restored upon exit from this method.
* Parsers using a {@link UnbufferedTokenStream} may not be able to
* perform the necessary save index() / seek(saved_index) operation.
*
* The trees are rooted at the node whose start..stop token indices
* include the start and stop indices of this ambiguity event. That is,
* the trees returns will always include the complete ambiguous subphrase
* identified by the ambiguity event.
*
* Be aware that this method does NOT notify error or parse listeners as
* it would trigger duplicate or otherwise unwanted events.
*
* This uses a temporary ParserATNSimulator and a ParserInterpreter
* so we don't mess up any statistics, event lists, etc...
* The parse tree constructed while identifying/making ambiguityInfo is
* not affected by this method as it creates a new parser interp to
* get the ambiguous interpretations.
*
* Nodes in the returned ambig trees are independent of the original parse
* tree (constructed while identifying/creating ambiguityInfo).
*
* @since 4.5.1
*
* @param originalParser The parser used to create ambiguityInfo; it
* is not modified by this routine and can be either
* a generated or interpreted parser. It's token
* stream *is* reset/seek()'d.
* @param ambiguityInfo The information about an ambiguous decision event
* for which you want ambiguous parse trees.
* @param startRuleIndex The start rule for the entire grammar, not
* the ambiguous decision. We re-parse the entire input
* and so we need the original start rule.
*
* @return The list of all possible interpretations of
* the input for the decision in ambiguityInfo.
* The actual interpretation chosen by the parser
* is always given first because this method
* retests the input in alternative order and
* ANTLR always resolves ambiguities by choosing
* the first alternative that matches the input.
*
* @throws RecognitionException Throws upon syntax error while matching
* ambig input.
*/
public static List<ParserRuleContext> getAmbiguousParseTrees(Parser originalParser,
AmbiguityInfo ambiguityInfo,
int startRuleIndex)
throws RecognitionException
{
List<ParserRuleContext> trees = new ArrayList<ParserRuleContext>();
int saveTokenInputPosition = originalParser.getTokenStream().index();
try {
// Create a new parser interpreter to parse the ambiguous subphrase
ParserInterpreter parser;
if ( originalParser instanceof ParserInterpreter ) {
parser = ((ParserInterpreter)originalParser).copyFrom((ParserInterpreter)originalParser);
}
else {
char[] serializedAtn = ATNSerializer.getSerializedAsChars(originalParser.getATN());
ATN deserialized = new ATNDeserializer().deserialize(serializedAtn);
parser = new ParserInterpreter(originalParser.getGrammarFileName(),
originalParser.getVocabulary(),
Arrays.asList(originalParser.getRuleNames()),
deserialized,
originalParser.getTokenStream());
}
// Make sure that we don't get any error messages from using this temporary parser
parser.removeErrorListeners();
parser.removeParseListeners();
parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);
// get ambig trees
int alt = ambiguityInfo.ambigAlts.nextSetBit(0);
while ( alt>=0 ) {
// re-parse entire input for all ambiguous alternatives
// (don't have to do first as it's been parsed, but do again for simplicity
// using this temp parser.)
parser.reset();
parser.getTokenStream().seek(0); // rewind the input all the way for re-parsing
parser.overrideDecision = ambiguityInfo.decision;
parser.overrideDecisionInputIndex = ambiguityInfo.startIndex;
parser.overrideDecisionAlt = alt;
ParserRuleContext t = parser.parse(startRuleIndex);
ParserRuleContext ambigSubTree =
Trees.getRootOfSubtreeEnclosingRegion(t, ambiguityInfo.startIndex, ambiguityInfo.stopIndex);
trees.add(ambigSubTree);
alt = ambiguityInfo.ambigAlts.nextSetBit(alt+1);
}
}
finally {
originalParser.getTokenStream().seek(saveTokenInputPosition);
}
return trees;
}
/**
* Checks whether or not {@code symbol} can follow the current state in the
* ATN. The behavior of this method is equivalent to the following, but is

View File

@ -48,7 +48,6 @@ import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.Pair;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
@ -100,6 +99,16 @@ public class ParserInterpreter extends Parser {
protected int overrideDecision = -1;
protected int overrideDecisionInputIndex = -1;
protected int overrideDecisionAlt = -1;
protected boolean overrideDecisionReached = false; // latch and only override once; error might trigger infinite loop
/** What is the current context when we override a decisions? This tells
* us what the root of the parse tree is when using override
* for an ambiguity/lookahead check.
*/
protected InterpreterRuleContext overrideDecisionRoot = null;
protected InterpreterRuleContext rootContext;
/**
* @deprecated Use {@link #ParserInterpreter(String, Vocabulary, Collection, ATN, TokenStream)} instead.
@ -138,17 +147,11 @@ public class ParserInterpreter extends Parser {
sharedContextCache));
}
/** A factory-like copy constructor that creates a new parser interpreter by reusing
* the fields of a previous interpreter.
*
* @since 4.5.1
*
* @param old The interpreter to copy
*/
public ParserInterpreter copyFrom(ParserInterpreter old) {
return new ParserInterpreter(old.grammarFileName, old.vocabulary,
Arrays.asList(old.ruleNames),
old.atn, old.getTokenStream());
@Override
public void reset() {
super.reset();
overrideDecisionReached = false;
overrideDecisionRoot = null;
}
@Override
@ -181,7 +184,7 @@ public class ParserInterpreter extends Parser {
public ParserRuleContext parse(int startRuleIndex) {
RuleStartState startRuleStartState = atn.ruleToStartState[startRuleIndex];
InterpreterRuleContext rootContext = createInterpreterRuleContext(null, ATNState.INVALID_STATE_NUMBER, startRuleIndex);
rootContext = createInterpreterRuleContext(null, ATNState.INVALID_STATE_NUMBER, startRuleIndex);
if (startRuleStartState.isLeftRecursiveRule) {
enterRecursionRule(rootContext, startRuleStartState.stateNumber, startRuleIndex, 0);
}
@ -239,12 +242,12 @@ public class ParserInterpreter extends Parser {
protected void visitState(ATNState p) {
// System.out.println("visitState "+p.stateNumber);
int edge = 1;
int predictedAlt = 1;
if ( p instanceof DecisionState ) {
edge = visitDecisionsState((DecisionState) p);
predictedAlt = visitDecisionState((DecisionState) p);
}
Transition transition = p.transition(edge - 1);
Transition transition = p.transition(predictedAlt - 1);
switch (transition.getSerializationType()) {
case Transition.EPSILON:
if ( p.getStateType()==ATNState.STAR_LOOP_ENTRY &&
@ -318,21 +321,22 @@ public class ParserInterpreter extends Parser {
setState(transition.target.stateNumber);
}
protected int visitDecisionsState(DecisionState p) {
int edge = 1;
protected int visitDecisionState(DecisionState p) {
int predictedAlt = 1;
if ( p.getNumberOfTransitions()>1 ) {
int predictedAlt;
getErrorHandler().sync(this);
int decision = p.decision;
if (decision == overrideDecision && _input.index() == overrideDecisionInputIndex) {
if ( !overrideDecisionReached &&
decision == overrideDecision && _input.index() == overrideDecisionInputIndex)
{
predictedAlt = overrideDecisionAlt;
overrideDecisionReached = true;
}
else {
predictedAlt = getInterpreter().adaptivePredict(_input, decision, _ctx);
}
edge = predictedAlt;
}
return edge;
return predictedAlt;
}
/** Provide simple "factory" for InterpreterRuleContext's. */
@ -405,6 +409,10 @@ public class ParserInterpreter extends Parser {
overrideDecisionAlt = forcedAlt;
}
public InterpreterRuleContext getOverrideDecisionRoot() {
return overrideDecisionRoot;
}
/** Rely on the error handler for this parser but, if no tokens are consumed
* to recover, add an error node. Otherwise, nothing is seen in the parse
* tree.
@ -418,10 +426,6 @@ public class ParserInterpreter extends Parser {
InputMismatchException ime = (InputMismatchException)e;
Token tok = e.getOffendingToken();
int expectedTokenType = ime.getExpectedTokens().getMinElement(); // get any element
String tokenText;
if ( expectedTokenType== Token.EOF ) tokenText = "<missing EOF>";
else tokenText = "<mismatched "+tok.getText()+">";
Token errToken =
getTokenFactory().create(new Pair<TokenSource, CharStream>(tok.getTokenSource(), tok.getTokenSource().getInputStream()),
expectedTokenType, tok.getText(),
@ -446,4 +450,8 @@ public class ParserInterpreter extends Parser {
protected Token recoverInline() {
return _errHandler.recoverInline(this);
}
public InterpreterRuleContext getRootContext() {
return rootContext;
}
}

View File

@ -398,6 +398,7 @@ public class ParserATNSimulator extends ATNSimulator {
* appropriate start state for the precedence level rather
* than simply setting DFA.s0.
*/
dfa.s0.configs = s0_closure; // not used for prediction but useful to know start configs anyway
s0_closure = applyPrecedenceFilter(s0_closure);
s0 = addDFAState(dfa, new DFAState(s0_closure));
dfa.setPrecedenceStartState(parser.getPrecedence(), s0);

View File

@ -244,4 +244,8 @@ public class ProfilingATNSimulator extends ParserATNSimulator {
public DecisionInfo[] getDecisionInfo() {
return decisions;
}
public DFAState getCurrentState() {
return currentState;
}
}

View File

@ -178,7 +178,6 @@ public class Trees {
/** Return a list of all ancestors of this node. The first node of
* list is the root and the last is the parent of this node.
*/
public static List<? extends Tree> getAncestors(Tree t) {
if ( t.getParent()==null ) return Collections.emptyList();
List<Tree> ancestors = new ArrayList<Tree>();
@ -190,6 +189,21 @@ public class Trees {
return ancestors;
}
/** Return true if t is u's parent or a node on path to root from u.
* Use == not equals().
*
* @since 4.5.1
*/
public static boolean isAncestorOf(Tree t, Tree u) {
if ( t==null || u==null || t.getParent()==null ) return false;
Tree p = u.getParent();
while ( p!=null ) {
if ( t == p ) return true;
p = p.getParent();
}
return false;
}
public static Collection<ParseTree> findAllTokenNodes(ParseTree t, int ttype) {
return findAllNodes(t, ttype, true);
}

View File

@ -32,6 +32,7 @@ package org.antlr.v4.tool;
import org.antlr.v4.Tool;
import org.antlr.v4.analysis.LeftRecursiveRuleTransformer;
import org.antlr.v4.automata.ParserATNFactory;
import org.antlr.v4.misc.CharSupport;
import org.antlr.v4.misc.OrderedHashMap;
import org.antlr.v4.misc.Utils;
@ -203,7 +204,6 @@ public class Grammar implements AttributeResolver {
public List<IntervalSet[]> decisionLOOK;
public final Tool tool;
/** Token names and literal tokens like "void" are uniquely indexed.
@ -507,6 +507,14 @@ public class Grammar implements AttributeResolver {
*/
}
public ATN getATN() {
if ( atn==null ) {
ParserATNFactory factory = new ParserATNFactory(this);
atn = factory.createATN();
}
return atn;
}
public Rule getRule(int index) { return indexToRule.get(index); }
public Rule getRule(String grammarName, String ruleName) {
@ -1313,6 +1321,16 @@ public class Grammar implements AttributeResolver {
return new LexerInterpreter(fileName, getVocabulary(), Arrays.asList(getRuleNames()), ((LexerGrammar)this).modes.keySet(), deserialized, input);
}
/** @since 4.5.1 */
public GrammarParserInterpreter createGrammarParserInterpreter(TokenStream tokenStream) {
if (this.isLexer()) {
throw new IllegalStateException("A parser interpreter can only be created for a parser or combined grammar.");
}
char[] serializedAtn = ATNSerializer.getSerializedAsChars(atn);
ATN deserialized = new ATNDeserializer().deserialize(serializedAtn);
return new GrammarParserInterpreter(this, deserialized, tokenStream);
}
public ParserInterpreter createParserInterpreter(TokenStream tokenStream) {
if (this.isLexer()) {
throw new IllegalStateException("A parser interpreter can only be created for a parser or combined grammar.");

View File

@ -0,0 +1,25 @@
package org.antlr.v4.tool;
import org.antlr.v4.runtime.InterpreterRuleContext;
import org.antlr.v4.runtime.ParserRuleContext;
/**
* @since 4.5.1
*/
public class GrammarInterpreterRuleContext extends InterpreterRuleContext {
protected int outerAltNum = 1;
public GrammarInterpreterRuleContext(ParserRuleContext parent, int invokingStateNumber, int ruleIndex) {
super(parent, invokingStateNumber, ruleIndex);
}
/** The predicted outermost alternative for the rule associated
* with this context object. If left recursive, the true original
* outermost alternative is returned.
*/
public int getOuterAltNum() { return outerAltNum; }
public void setOuterAltNum(int outerAltNum) {
this.outerAltNum = outerAltNum;
}
}

View File

@ -0,0 +1,292 @@
package org.antlr.v4.tool;
import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.InterpreterRuleContext;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserInterpreter;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.UnbufferedTokenStream;
import org.antlr.v4.runtime.Vocabulary;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNDeserializer;
import org.antlr.v4.runtime.atn.ATNSerializer;
import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.atn.RuleStartState;
import org.antlr.v4.runtime.atn.StarLoopEntryState;
import org.antlr.v4.runtime.tree.Trees;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GrammarParserInterpreter extends ParserInterpreter {
/** The grammar associated with this interpreter. Unlike the
* {@link ParserInterpreter} from the standard distribution,
* this can reference Grammar, which is in the tools area not
* purely runtime.
*/
protected final Grammar g;
protected BitSet decisionStatesThatSetOuterAltNumInContext;
/** Cache {@link LeftRecursiveRule#getPrimaryAlts()} and
* {@link LeftRecursiveRule#getRecursiveOpAlts()} for states in
* {@link #decisionStatesThatSetOuterAltNumInContext}.
*/
protected final Map<DecisionState, int[]> stateToAltsMap = new HashMap<DecisionState, int[]>();
public GrammarParserInterpreter(Grammar g,
String grammarFileName,
Vocabulary vocabulary,
Collection<String> ruleNames,
ATN atn,
TokenStream input) {
super(grammarFileName, vocabulary, ruleNames, atn, input);
this.g = g;
}
public GrammarParserInterpreter(Grammar g, ATN atn, TokenStream input) {
super(g.fileName, g.getVocabulary(),
Arrays.asList(g.getRuleNames()),
atn, // must run ATN through serializer to set some state flags
input);
this.g = g;
decisionStatesThatSetOuterAltNumInContext = findOuterMostDecisionStates();
}
@Override
protected InterpreterRuleContext createInterpreterRuleContext(ParserRuleContext parent,
int invokingStateNumber,
int ruleIndex)
{
return new GrammarInterpreterRuleContext(parent, invokingStateNumber, ruleIndex);
}
@Override
public void reset() {
super.reset();
overrideDecisionRoot = null;
}
/** identify the ATN states where we need to set the outer alt number.
* For regular rules, that's the block at the target to rule start state.
* For left-recursive rules, we track the primary block, which looks just
* like a regular rule's outer block, and the star loop block (always
* there even if 1 alt).
*/
public BitSet findOuterMostDecisionStates() {
BitSet track = new BitSet(atn.states.size());
int numberOfDecisions = atn.getNumberOfDecisions();
for (int i = 0; i < numberOfDecisions; i++) {
DecisionState decisionState = atn.getDecisionState(i);
RuleStartState startState = atn.ruleToStartState[decisionState.ruleIndex];
// Look for StarLoopEntryState that is in any left recursive rule
if ( decisionState instanceof StarLoopEntryState) {
StarLoopEntryState loopEntry = (StarLoopEntryState)decisionState;
if ( loopEntry.isPrecedenceDecision ) {
// Recursive alts always result in a (...)* in the transformed
// left recursive rule and that always has a BasicBlockStartState
// even if just 1 recursive alt exists.
ATNState blockStart = loopEntry.transition(0).target;
// track the StarBlockStartState associated with the recursive alternatives
track.set(blockStart.stateNumber);
}
}
else if ( startState.transition(0).target == decisionState ) {
// always track outermost block for any rule if it exists
track.set(decisionState.stateNumber);
}
}
return track;
}
/**In the case of left-recursive rules,
* there is typically a decision for the primary alternatives and a
* decision to choose between the recursive operator alternatives.
* For example, the following left recursive rule has two primary and 2
* recursive alternatives.</p>
*
e : e '*' e
| '-' INT
| e '+' e
| ID
;
* <p>ANTLR rewrites that rule to be</p>
e[int precedence]
: ('-' INT | ID)
( {...}? '*' e[5]
| {...}? '+' e[3]
)*
;
*
* <p>So, there are two decisions associated with picking the outermost alt.
* This complicates our tracking significantly. The outermost alternative number
* is a function of the decision (ATN state) within a left recursive rule and the
* predicted alternative coming back from adaptivePredict().
*/
@Override
protected int visitDecisionState(DecisionState p) {
int predictedAlt = super.visitDecisionState(p);
if( p.getNumberOfTransitions() > 1) {
// System.out.print("decision "+p.decision+": "+predictedAlt);
if( p.decision == this.overrideDecision &&
this._input.index() == this.overrideDecisionInputIndex)
{
// System.out.print(" OVERRIDE");
overrideDecisionRoot = (GrammarInterpreterRuleContext)getContext();
}
// System.out.println();
}
GrammarInterpreterRuleContext ctx = (GrammarInterpreterRuleContext)_ctx;
if ( decisionStatesThatSetOuterAltNumInContext.get(p.stateNumber) ) {
ctx.outerAltNum = predictedAlt;
Rule r = g.getRule(p.ruleIndex);
if ( atn.ruleToStartState[r.index].isLeftRecursiveRule ) {
int[] alts = stateToAltsMap.get(p);
LeftRecursiveRule lr = (LeftRecursiveRule) g.getRule(p.ruleIndex);
if (p.getStateType() == ATNState.BLOCK_START) {
if ( alts==null ) {
alts = lr.getPrimaryAlts();
stateToAltsMap.put(p, alts); // cache it
}
}
else if (p.getStateType() == ATNState.STAR_BLOCK_START) {
if ( alts==null ) {
alts = lr.getRecursiveOpAlts();
stateToAltsMap.put(p, alts); // cache it
}
}
ctx.outerAltNum = alts[predictedAlt];
}
}
return predictedAlt;
}
/** Given an AmbiguityInfo object that contains information about an
* ambiguous decision event, return the list of ambiguous parse trees.
* An ambiguity occurs when a specific token sequence can be recognized
* in more than one way by the grammar. These ambiguities are detected only
* at decision points.
*
* The list of trees includes the actual interpretation (that for
* the minimum alternative number) and all ambiguous alternatives.
* The actual interpretation is always first.
*
* This method reuses the same physical input token stream used to
* detect the ambiguity by the original parser in the first place.
* This method resets/seeks within but does not alter originalParser.
* The input position is restored upon exit from this method.
* Parsers using a {@link UnbufferedTokenStream} may not be able to
* perform the necessary save index() / seek(saved_index) operation.
*
* The trees are rooted at the node whose start..stop token indices
* include the start and stop indices of this ambiguity event. That is,
* the trees returns will always include the complete ambiguous subphrase
* identified by the ambiguity event.
*
* Be aware that this method does NOT notify error or parse listeners as
* it would trigger duplicate or otherwise unwanted events.
*
* This uses a temporary ParserATNSimulator and a ParserInterpreter
* so we don't mess up any statistics, event lists, etc...
* The parse tree constructed while identifying/making ambiguityInfo is
* not affected by this method as it creates a new parser interp to
* get the ambiguous interpretations.
*
* Nodes in the returned ambig trees are independent of the original parse
* tree (constructed while identifying/creating ambiguityInfo).
*
* @since 4.5.1
*
* @param originalParser The parser used to create ambiguityInfo; it
* is not modified by this routine and can be either
* a generated or interpreted parser. It's token
* stream *is* reset/seek()'d.
* @param ambiguityInfo The information about an ambiguous decision event
* for which you want ambiguous parse trees.
* @param startRuleIndex The start rule for the entire grammar, not
* the ambiguous decision. We re-parse the entire input
* and so we need the original start rule.
*
* @return The list of all possible interpretations of
* the input for the decision in ambiguityInfo.
* The actual interpretation chosen by the parser
* is always given first because this method
* retests the input in alternative order and
* ANTLR always resolves ambiguities by choosing
* the first alternative that matches the input.
*
* @throws RecognitionException Throws upon syntax error while matching
* ambig input.
*/
public static List<ParserRuleContext> getAllPossibleParseTrees(Grammar g,
Parser originalParser,
TokenStream tokens,
int decision,
BitSet alts,
int startIndex,
int stopIndex,
int startRuleIndex)
throws RecognitionException
{
List<ParserRuleContext> trees = new ArrayList<ParserRuleContext>();
// Create a new parser interpreter to parse the ambiguous subphrase
ParserInterpreter parser;
if (originalParser instanceof ParserInterpreter) {
parser = new GrammarParserInterpreter(g, originalParser.getATN(), originalParser.getTokenStream());
}
else {
char[] serializedAtn = ATNSerializer.getSerializedAsChars(originalParser.getATN());
ATN deserialized = new ATNDeserializer().deserialize(serializedAtn);
parser = new ParserInterpreter(originalParser.getGrammarFileName(),
originalParser.getVocabulary(),
Arrays.asList(originalParser.getRuleNames()),
deserialized,
tokens);
}
parser.setInputStream(tokens);
// Make sure that we don't get any error messages from using this temporary parser
parser.setErrorHandler(new BailErrorStrategy());
parser.removeErrorListeners();
parser.removeParseListeners();
parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);
// get ambig trees
int alt = alts.nextSetBit(0);
while (alt >= 0) {
// re-parse entire input for all ambiguous alternatives
// (don't have to do first as it's been parsed, but do again for simplicity
// using this temp parser.)
parser.reset();
parser.getTokenStream().seek(0); // rewind the input all the way for re-parsing
parser.addDecisionOverride(decision, startIndex, alt);
ParserRuleContext t = parser.parse(startRuleIndex);
GrammarInterpreterRuleContext ambigSubTree =
(GrammarInterpreterRuleContext) Trees.getRootOfSubtreeEnclosingRegion(t, startIndex, stopIndex);
// Use higher of overridden decision tree or tree enclosing all tokens
if ( Trees.isAncestorOf(parser.getOverrideDecisionRoot(), ambigSubTree) ) {
ambigSubTree = (GrammarInterpreterRuleContext)parser.getOverrideDecisionRoot();
}
trees.add(ambigSubTree);
alt = alts.nextSetBit(alt + 1);
}
return trees;
}
}

View File

@ -3,7 +3,6 @@ package org.antlr.v4.test.tool;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.LexerInterpreter;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserInterpreter;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.atn.ATNState;
@ -16,6 +15,7 @@ import org.antlr.v4.runtime.atn.RuleStartState;
import org.antlr.v4.runtime.atn.Transition;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.GrammarParserInterpreter;
import org.antlr.v4.tool.LexerGrammar;
import org.junit.Test;
@ -211,7 +211,7 @@ public class TestAmbigParseTrees {
{
LexerInterpreter lexEngine = lg.createLexerInterpreter(new ANTLRInputStream(input));
CommonTokenStream tokens = new CommonTokenStream(lexEngine);
final ParserInterpreter parser = g.createParserInterpreter(tokens);
final GrammarParserInterpreter parser = g.createGrammarParserInterpreter(tokens);
parser.setProfile(true);
parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);
@ -226,7 +226,15 @@ public class TestAmbigParseTrees {
assertEquals(1, ambiguities.size());
AmbiguityInfo ambiguityInfo = ambiguities.get(0);
List<ParserRuleContext> ambiguousParseTrees = Parser.getAmbiguousParseTrees(parser, ambiguityInfo, ruleIndex);
List<ParserRuleContext> ambiguousParseTrees =
GrammarParserInterpreter.getAllPossibleParseTrees(g,
parser,
tokens,
ambiguityInfo.decision,
ambiguityInfo.ambigAlts,
ambiguityInfo.startIndex,
ambiguityInfo.stopIndex,
ruleIndex);
assertEquals(expectedAmbigAlts, ambiguityInfo.ambigAlts.toString());
assertEquals(ambiguityInfo.ambigAlts.cardinality(), ambiguousParseTrees.size());