antlr4ts
Version:
ANTLR 4 runtime for JavaScript written in Typescript
977 lines (976 loc) • 111 kB
JavaScript
"use strict";
/*!
* Copyright 2016 The ANTLR Project. All rights reserved.
* Licensed under the BSD-3-Clause license. See LICENSE file in the project root for license information.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParserATNSimulator = void 0;
// ConvertTo-TS run at 2016-10-04T11:26:31.1989835-07:00
const AcceptStateInfo_1 = require("../dfa/AcceptStateInfo");
const ActionTransition_1 = require("./ActionTransition");
const Array2DHashSet_1 = require("../misc/Array2DHashSet");
const Arrays_1 = require("../misc/Arrays");
const ATN_1 = require("./ATN");
const ATNConfig_1 = require("./ATNConfig");
const ATNConfigSet_1 = require("./ATNConfigSet");
const ATNSimulator_1 = require("./ATNSimulator");
const ATNStateType_1 = require("./ATNStateType");
const AtomTransition_1 = require("./AtomTransition");
const BitSet_1 = require("../misc/BitSet");
const ConflictInfo_1 = require("./ConflictInfo");
const DecisionState_1 = require("./DecisionState");
const DFAState_1 = require("../dfa/DFAState");
const IntegerList_1 = require("../misc/IntegerList");
const Interval_1 = require("../misc/Interval");
const IntStream_1 = require("../IntStream");
const Decorators_1 = require("../Decorators");
const NotSetTransition_1 = require("./NotSetTransition");
const NoViableAltException_1 = require("../NoViableAltException");
const ObjectEqualityComparator_1 = require("../misc/ObjectEqualityComparator");
const ParserRuleContext_1 = require("../ParserRuleContext");
const PredictionContext_1 = require("./PredictionContext");
const PredictionContextCache_1 = require("./PredictionContextCache");
const PredictionMode_1 = require("./PredictionMode");
const RuleStopState_1 = require("./RuleStopState");
const RuleTransition_1 = require("./RuleTransition");
const SemanticContext_1 = require("./SemanticContext");
const SetTransition_1 = require("./SetTransition");
const SimulatorState_1 = require("./SimulatorState");
const Token_1 = require("../Token");
const VocabularyImpl_1 = require("../VocabularyImpl");
const assert = require("assert");
const MAX_SHORT_VALUE = 0xFFFF;
const MIN_INTEGER_VALUE = -((1 << 31) >>> 0);
/**
* The embodiment of the adaptive LL(*), ALL(*), parsing strategy.
*
* The basic complexity of the adaptive strategy makes it harder to understand.
* We begin with ATN simulation to build paths in a DFA. Subsequent prediction
* requests go through the DFA first. If they reach a state without an edge for
* the current symbol, the algorithm fails over to the ATN simulation to
* complete the DFA path for the current input (until it finds a conflict state
* or uniquely predicting state).
*
* All of that is done without using the outer context because we want to create
* a DFA that is not dependent upon the rule invocation stack when we do a
* prediction. One DFA works in all contexts. We avoid using context not
* necessarily because it's slower, although it can be, but because of the DFA
* caching problem. The closure routine only considers the rule invocation stack
* created during prediction beginning in the decision rule. For example, if
* prediction occurs without invoking another rule's ATN, there are no context
* stacks in the configurations. When lack of context leads to a conflict, we
* don't know if it's an ambiguity or a weakness in the strong LL(*) parsing
* strategy (versus full LL(*)).
*
* When SLL yields a configuration set with conflict, we rewind the input and
* retry the ATN simulation, this time using full outer context without adding
* to the DFA. Configuration context stacks will be the full invocation stacks
* from the start rule. If we get a conflict using full context, then we can
* definitively say we have a true ambiguity for that input sequence. If we
* don't get a conflict, it implies that the decision is sensitive to the outer
* context. (It is not context-sensitive in the sense of context-sensitive
* grammars.)
*
* The next time we reach this DFA state with an SLL conflict, through DFA
* simulation, we will again retry the ATN simulation using full context mode.
* This is slow because we can't save the results and have to "interpret" the
* ATN each time we get that input.
*
* **CACHING FULL CONTEXT PREDICTIONS**
*
* We could cache results from full context to predicted alternative easily and
* that saves a lot of time but doesn't work in presence of predicates. The set
* of visible predicates from the ATN start state changes depending on the
* context, because closure can fall off the end of a rule. I tried to cache
* tuples (stack context, semantic context, predicted alt) but it was slower
* than interpreting and much more complicated. Also required a huge amount of
* memory. The goal is not to create the world's fastest parser anyway. I'd like
* to keep this algorithm simple. By launching multiple threads, we can improve
* the speed of parsing across a large number of files.
*
* There is no strict ordering between the amount of input used by SLL vs LL,
* which makes it really hard to build a cache for full context. Let's say that
* we have input A B C that leads to an SLL conflict with full context X. That
* implies that using X we might only use A B but we could also use A B C D to
* resolve conflict. Input A B C D could predict alternative 1 in one position
* in the input and A B C E could predict alternative 2 in another position in
* input. The conflicting SLL configurations could still be non-unique in the
* full context prediction, which would lead us to requiring more input than the
* original A B C. To make a prediction cache work, we have to track the exact
* input used during the previous prediction. That amounts to a cache that maps
* X to a specific DFA for that context.
*
* Something should be done for left-recursive expression predictions. They are
* likely LL(1) + pred eval. Easier to do the whole SLL unless error and retry
* with full LL thing Sam does.
*
* **AVOIDING FULL CONTEXT PREDICTION**
*
* We avoid doing full context retry when the outer context is empty, we did not
* dip into the outer context by falling off the end of the decision state rule,
* or when we force SLL mode.
*
* As an example of the not dip into outer context case, consider as super
* constructor calls versus function calls. One grammar might look like
* this:
*
* ```antlr
* ctorBody
* : '{' superCall? stat* '}'
* ;
* ```
*
* Or, you might see something like
*
* ```antlr
* stat
* : superCall ';'
* | expression ';'
* | ...
* ;
* ```
*
* In both cases I believe that no closure operations will dip into the outer
* context. In the first case ctorBody in the worst case will stop at the '}'.
* In the 2nd case it should stop at the ';'. Both cases should stay within the
* entry rule and not dip into the outer context.
*
* **PREDICATES**
*
* Predicates are always evaluated if present in either SLL or LL both. SLL and
* LL simulation deals with predicates differently. SLL collects predicates as
* it performs closure operations like ANTLR v3 did. It delays predicate
* evaluation until it reaches and accept state. This allows us to cache the SLL
* ATN simulation whereas, if we had evaluated predicates on-the-fly during
* closure, the DFA state configuration sets would be different and we couldn't
* build up a suitable DFA.
*
* When building a DFA accept state during ATN simulation, we evaluate any
* predicates and return the sole semantically valid alternative. If there is
* more than 1 alternative, we report an ambiguity. If there are 0 alternatives,
* we throw an exception. Alternatives without predicates act like they have
* true predicates. The simple way to think about it is to strip away all
* alternatives with false predicates and choose the minimum alternative that
* remains.
*
* When we start in the DFA and reach an accept state that's predicated, we test
* those and return the minimum semantically viable alternative. If no
* alternatives are viable, we throw an exception.
*
* During full LL ATN simulation, closure always evaluates predicates and
* on-the-fly. This is crucial to reducing the configuration set size during
* closure. It hits a landmine when parsing with the Java grammar, for example,
* without this on-the-fly evaluation.
*
* **SHARING DFA**
*
* All instances of the same parser share the same decision DFAs through a
* static field. Each instance gets its own ATN simulator but they share the
* same {@link ATN#decisionToDFA} field. They also share a
* {@link PredictionContextCache} object that makes sure that all
* {@link PredictionContext} objects are shared among the DFA states. This makes
* a big size difference.
*
* **THREAD SAFETY**
*
* The {@link ParserATNSimulator} locks on the {@link ATN#decisionToDFA} field when
* it adds a new DFA object to that array. {@link #addDFAEdge}
* locks on the DFA for the current decision when setting the
* {@link DFAState#edges} field. {@link #addDFAState} locks on
* the DFA for the current decision when looking up a DFA state to see if it
* already exists. We must make sure that all requests to add DFA states that
* are equivalent result in the same shared DFA object. This is because lots of
* threads will be trying to update the DFA at once. The
* {@link #addDFAState} method also locks inside the DFA lock
* but this time on the shared context cache when it rebuilds the
* configurations' {@link PredictionContext} objects using cached
* subgraphs/nodes. No other locking occurs, even during DFA simulation. This is
* safe as long as we can guarantee that all threads referencing
* `s.edge[t]` get the same physical target {@link DFAState}, or
* `undefined`. Once into the DFA, the DFA simulation does not reference the
* {@link DFA#states} map. It follows the {@link DFAState#edges} field to new
* targets. The DFA simulator will either find {@link DFAState#edges} to be
* `undefined`, to be non-`undefined` and `dfa.edges[t]` undefined, or
* `dfa.edges[t]` to be non-undefined. The
* {@link #addDFAEdge} method could be racing to set the field
* but in either case the DFA simulator works; if `undefined`, and requests ATN
* simulation. It could also race trying to get `dfa.edges[t]`, but either
* way it will work because it's not doing a test and set operation.
*
* **Starting with SLL then failing to combined SLL/LL (Two-Stage
* Parsing)**
*
* Sam pointed out that if SLL does not give a syntax error, then there is no
* point in doing full LL, which is slower. We only have to try LL if we get a
* syntax error. For maximum speed, Sam starts the parser set to pure SLL
* mode with the {@link BailErrorStrategy}:
*
* ```
* parser.interpreter.{@link #setPredictionMode setPredictionMode}`(`{@link PredictionMode#SLL}`)`;
* parser.{@link Parser#setErrorHandler setErrorHandler}(new {@link BailErrorStrategy}());
* ```
*
* If it does not get a syntax error, then we're done. If it does get a syntax
* error, we need to retry with the combined SLL/LL strategy.
*
* The reason this works is as follows. If there are no SLL conflicts, then the
* grammar is SLL (at least for that input set). If there is an SLL conflict,
* the full LL analysis must yield a set of viable alternatives which is a
* subset of the alternatives reported by SLL. If the LL set is a singleton,
* then the grammar is LL but not SLL. If the LL set is the same size as the SLL
* set, the decision is SLL. If the LL set has size > 1, then that decision
* is truly ambiguous on the current input. If the LL set is smaller, then the
* SLL conflict resolution might choose an alternative that the full LL would
* rule out as a possibility based upon better context information. If that's
* the case, then the SLL parse will definitely get an error because the full LL
* analysis says it's not viable. If SLL conflict resolution chooses an
* alternative within the LL set, them both SLL and LL would choose the same
* alternative because they both choose the minimum of multiple conflicting
* alternatives.
*
* Let's say we have a set of SLL conflicting alternatives `{1, 2, 3}` and
* a smaller LL set called *s*. If *s* is `{2, 3}`, then SLL
* parsing will get an error because SLL will pursue alternative 1. If
* *s* is `{1, 2}` or `{1, 3}` then both SLL and LL will
* choose the same alternative because alternative one is the minimum of either
* set. If *s* is `{2}` or `{3}` then SLL will get a syntax
* error. If *s* is `{1}` then SLL will succeed.
*
* Of course, if the input is invalid, then we will get an error for sure in
* both SLL and LL parsing. Erroneous input will therefore require 2 passes over
* the input.
*/
let ParserATNSimulator = class ParserATNSimulator extends ATNSimulator_1.ATNSimulator {
constructor(atn, parser) {
super(atn);
this.predictionMode = PredictionMode_1.PredictionMode.LL;
this.force_global_context = false;
this.always_try_local_context = true;
/**
* Determines whether the DFA is used for full-context predictions. When
* `true`, the DFA stores transition information for both full-context
* and SLL parsing; otherwise, the DFA only stores SLL transition
* information.
*
* For some grammars, enabling the full-context DFA can result in a
* substantial performance improvement. However, this improvement typically
* comes at the expense of memory used for storing the cached DFA states,
* configuration sets, and prediction contexts.
*
* The default value is `false`.
*/
this.enable_global_context_dfa = false;
this.optimize_unique_closure = true;
this.optimize_ll1 = true;
this.optimize_tail_calls = true;
this.tail_call_preserves_sll = true;
this.treat_sllk1_conflict_as_ambiguity = false;
/**
* When `true`, ambiguous alternatives are reported when they are
* encountered within {@link #execATN}. When `false`, these messages
* are suppressed. The default is `false`.
*
* When messages about ambiguous alternatives are not required, setting this
* to `false` enables additional internal optimizations which may lose
* this information.
*/
this.reportAmbiguities = false;
/** By default we do full context-sensitive LL(*) parsing not
* Strong LL(*) parsing. If we fail with Strong LL(*) we
* try full LL(*). That means we rewind and use context information
* when closure operations fall off the end of the rule that
* holds the decision were evaluating.
*/
this.userWantsCtxSensitive = true;
this._parser = parser;
}
getPredictionMode() {
return this.predictionMode;
}
setPredictionMode(predictionMode) {
this.predictionMode = predictionMode;
}
reset() {
// intentionally empty
}
adaptivePredict(input, decision, outerContext, useContext) {
if (useContext === undefined) {
useContext = false;
}
let dfa = this.atn.decisionToDFA[decision];
assert(dfa != null);
if (this.optimize_ll1 && !dfa.isPrecedenceDfa && !dfa.isEmpty) {
let ll_1 = input.LA(1);
if (ll_1 >= 0 && ll_1 <= 0xFFFF) {
let key = ((decision << 16) >>> 0) + ll_1;
let alt = this.atn.LL1Table.get(key);
if (alt != null) {
return alt;
}
}
}
this.dfa = dfa;
if (this.force_global_context) {
useContext = true;
}
else if (!this.always_try_local_context) {
useContext = useContext || dfa.isContextSensitive;
}
this.userWantsCtxSensitive = useContext || (this.predictionMode !== PredictionMode_1.PredictionMode.SLL && outerContext != null && !this.atn.decisionToState[decision].sll);
if (outerContext == null) {
outerContext = ParserRuleContext_1.ParserRuleContext.emptyContext();
}
let state;
if (!dfa.isEmpty) {
state = this.getStartState(dfa, input, outerContext, useContext);
}
if (state == null) {
if (outerContext == null) {
outerContext = ParserRuleContext_1.ParserRuleContext.emptyContext();
}
if (ParserATNSimulator.debug) {
console.log("ATN decision " + dfa.decision +
" exec LA(1)==" + this.getLookaheadName(input) +
", outerContext=" + outerContext.toString(this._parser));
}
state = this.computeStartState(dfa, outerContext, useContext);
}
let m = input.mark();
let index = input.index;
try {
let alt = this.execDFA(dfa, input, index, state);
if (ParserATNSimulator.debug) {
console.log("DFA after predictATN: " + dfa.toString(this._parser.vocabulary, this._parser.ruleNames));
}
return alt;
}
finally {
this.dfa = undefined;
input.seek(index);
input.release(m);
}
}
getStartState(dfa, input, outerContext, useContext) {
if (!useContext) {
if (dfa.isPrecedenceDfa) {
// the start state for a precedence DFA depends on the current
// parser precedence, and is provided by a DFA method.
let state = dfa.getPrecedenceStartState(this._parser.precedence, false);
if (state == null) {
return undefined;
}
return new SimulatorState_1.SimulatorState(outerContext, state, false, outerContext);
}
else {
if (dfa.s0 == null) {
return undefined;
}
return new SimulatorState_1.SimulatorState(outerContext, dfa.s0, false, outerContext);
}
}
if (!this.enable_global_context_dfa) {
return undefined;
}
let remainingContext = outerContext;
assert(outerContext != null);
let s0;
if (dfa.isPrecedenceDfa) {
s0 = dfa.getPrecedenceStartState(this._parser.precedence, true);
}
else {
s0 = dfa.s0full;
}
while (remainingContext != null && s0 != null && s0.isContextSensitive) {
remainingContext = this.skipTailCalls(remainingContext);
s0 = s0.getContextTarget(this.getReturnState(remainingContext));
if (remainingContext.isEmpty) {
assert(s0 == null || !s0.isContextSensitive);
}
else {
remainingContext = remainingContext.parent;
}
}
if (s0 == null) {
return undefined;
}
return new SimulatorState_1.SimulatorState(outerContext, s0, useContext, remainingContext);
}
execDFA(dfa, input, startIndex, state) {
let outerContext = state.outerContext;
if (ParserATNSimulator.dfa_debug) {
console.log("DFA decision " + dfa.decision +
" exec LA(1)==" + this.getLookaheadName(input) +
", outerContext=" + outerContext.toString(this._parser));
}
if (ParserATNSimulator.dfa_debug) {
console.log(dfa.toString(this._parser.vocabulary, this._parser.ruleNames));
}
let s = state.s0;
let t = input.LA(1);
let remainingOuterContext = state.remainingOuterContext;
while (true) {
if (ParserATNSimulator.dfa_debug) {
console.log("DFA state " + s.stateNumber + " LA(1)==" + this.getLookaheadName(input));
}
if (state.useContext) {
while (s.isContextSymbol(t)) {
let next;
if (remainingOuterContext != null) {
remainingOuterContext = this.skipTailCalls(remainingOuterContext);
next = s.getContextTarget(this.getReturnState(remainingOuterContext));
}
if (next == null) {
// fail over to ATN
let initialState = new SimulatorState_1.SimulatorState(state.outerContext, s, state.useContext, remainingOuterContext);
return this.execATN(dfa, input, startIndex, initialState);
}
assert(remainingOuterContext != null);
remainingOuterContext = remainingOuterContext.parent;
s = next;
}
}
if (this.isAcceptState(s, state.useContext)) {
if (s.predicates != null) {
if (ParserATNSimulator.dfa_debug) {
console.log("accept " + s);
}
}
else {
if (ParserATNSimulator.dfa_debug) {
console.log("accept; predict " + s.prediction + " in state " + s.stateNumber);
}
}
// keep going unless we're at EOF or state only has one alt number
// mentioned in configs; check if something else could match
// TODO: don't we always stop? only lexer would keep going
// TODO: v3 dfa don't do this.
break;
}
// t is not updated if one of these states is reached
assert(!this.isAcceptState(s, state.useContext));
// if no edge, pop over to ATN interpreter, update DFA and return
let target = this.getExistingTargetState(s, t);
if (target == null) {
if (ParserATNSimulator.dfa_debug && t >= 0) {
console.log("no edge for " + this._parser.vocabulary.getDisplayName(t));
}
let alt;
if (ParserATNSimulator.dfa_debug) {
let interval = Interval_1.Interval.of(startIndex, this._parser.inputStream.index);
console.log("ATN exec upon " +
this._parser.inputStream.getText(interval) +
" at DFA state " + s.stateNumber);
}
let initialState = new SimulatorState_1.SimulatorState(outerContext, s, state.useContext, remainingOuterContext);
alt = this.execATN(dfa, input, startIndex, initialState);
if (ParserATNSimulator.dfa_debug) {
console.log("back from DFA update, alt=" + alt + ", dfa=\n" + dfa.toString(this._parser.vocabulary, this._parser.ruleNames));
//dump(dfa);
}
// action already executed
if (ParserATNSimulator.dfa_debug) {
console.log("DFA decision " + dfa.decision +
" predicts " + alt);
}
return alt; // we've updated DFA, exec'd action, and have our deepest answer
}
else if (target === ATNSimulator_1.ATNSimulator.ERROR) {
let errorState = new SimulatorState_1.SimulatorState(outerContext, s, state.useContext, remainingOuterContext);
return this.handleNoViableAlt(input, startIndex, errorState);
}
s = target;
if (!this.isAcceptState(s, state.useContext) && t !== IntStream_1.IntStream.EOF) {
input.consume();
t = input.LA(1);
}
}
// if ( acceptState==null ) {
// if ( debug ) System.out.println("!!! no viable alt in dfa");
// return -1;
// }
if (!state.useContext && s.configs.conflictInfo != null) {
if (dfa.atnStartState instanceof DecisionState_1.DecisionState) {
if (!this.userWantsCtxSensitive ||
(!s.configs.dipsIntoOuterContext && s.configs.isExactConflict) ||
(this.treat_sllk1_conflict_as_ambiguity && input.index === startIndex)) {
// we don't report the ambiguity again
//if ( !this.acceptState.configset.hasSemanticContext ) {
// this.reportAmbiguity(dfa, acceptState, startIndex, input.index, acceptState.configset.conflictingAlts, acceptState.configset);
//}
}
else {
assert(!state.useContext);
// Before attempting full context prediction, check to see if there are
// disambiguating or validating predicates to evaluate which allow an
// immediate decision
let conflictingAlts;
let predicates = s.predicates;
if (predicates != null) {
let conflictIndex = input.index;
if (conflictIndex !== startIndex) {
input.seek(startIndex);
}
conflictingAlts = this.evalSemanticContext(predicates, outerContext, true);
if (conflictingAlts.cardinality() === 1) {
return conflictingAlts.nextSetBit(0);
}
if (conflictIndex !== startIndex) {
// restore the index so reporting the fallback to full
// context occurs with the index at the correct spot
input.seek(conflictIndex);
}
}
if (this.reportAmbiguities) {
let conflictState = new SimulatorState_1.SimulatorState(outerContext, s, state.useContext, remainingOuterContext);
this.reportAttemptingFullContext(dfa, conflictingAlts, conflictState, startIndex, input.index);
}
input.seek(startIndex);
return this.adaptivePredict(input, dfa.decision, outerContext, true);
}
}
}
// Before jumping to prediction, check to see if there are
// disambiguating or validating predicates to evaluate
let predicates = s.predicates;
if (predicates != null) {
let stopIndex = input.index;
if (startIndex !== stopIndex) {
input.seek(startIndex);
}
let alts = this.evalSemanticContext(predicates, outerContext, this.reportAmbiguities && this.predictionMode === PredictionMode_1.PredictionMode.LL_EXACT_AMBIG_DETECTION);
switch (alts.cardinality()) {
case 0:
throw this.noViableAlt(input, outerContext, s.configs, startIndex);
case 1:
return alts.nextSetBit(0);
default:
// report ambiguity after predicate evaluation to make sure the correct
// set of ambig alts is reported.
if (startIndex !== stopIndex) {
input.seek(stopIndex);
}
this.reportAmbiguity(dfa, s, startIndex, stopIndex, s.configs.isExactConflict, alts, s.configs);
return alts.nextSetBit(0);
}
}
if (ParserATNSimulator.dfa_debug) {
console.log("DFA decision " + dfa.decision +
" predicts " + s.prediction);
}
return s.prediction;
}
/**
* Determines if a particular DFA state should be treated as an accept state
* for the current prediction mode. In addition to the `useContext`
* parameter, the {@link #getPredictionMode()} method provides the
* prediction mode controlling the prediction algorithm as a whole.
*
* The default implementation simply returns the value of
* `DFAState.isAcceptState` except for conflict states when
* `useContext` is `true` and {@link #getPredictionMode()} is
* {@link PredictionMode#LL_EXACT_AMBIG_DETECTION}. In that case, only
* conflict states where {@link ATNConfigSet#isExactConflict} is
* `true` are considered accept states.
*
* @param state The DFA state to check.
* @param useContext `true` if the prediction algorithm is currently
* considering the full parser context; otherwise, `false` if the
* algorithm is currently performing a local context prediction.
*
* @returns `true` if the specified `state` is an accept state;
* otherwise, `false`.
*/
isAcceptState(state, useContext) {
if (!state.isAcceptState) {
return false;
}
if (state.configs.conflictingAlts == null) {
// unambiguous
return true;
}
// More picky when we need exact conflicts
if (useContext && this.predictionMode === PredictionMode_1.PredictionMode.LL_EXACT_AMBIG_DETECTION) {
return state.configs.isExactConflict;
}
return true;
}
/** Performs ATN simulation to compute a predicted alternative based
* upon the remaining input, but also updates the DFA cache to avoid
* having to traverse the ATN again for the same input sequence.
*
* There are some key conditions we're looking for after computing a new
* set of ATN configs (proposed DFA state):
*
* * if the set is empty, there is no viable alternative for current symbol
* * does the state uniquely predict an alternative?
* * does the state have a conflict that would prevent us from
* putting it on the work list?
* * if in non-greedy decision is there a config at a rule stop state?
*
* We also have some key operations to do:
*
* * add an edge from previous DFA state to potentially new DFA state, D,
* upon current symbol but only if adding to work list, which means in all
* cases except no viable alternative (and possibly non-greedy decisions?)
* * collecting predicates and adding semantic context to DFA accept states
* * adding rule context to context-sensitive DFA accept states
* * consuming an input symbol
* * reporting a conflict
* * reporting an ambiguity
* * reporting a context sensitivity
* * reporting insufficient predicates
*
* We should isolate those operations, which are side-effecting, to the
* main work loop. We can isolate lots of code into other functions, but
* they should be side effect free. They can return package that
* indicates whether we should report something, whether we need to add a
* DFA edge, whether we need to augment accept state with semantic
* context or rule invocation context. Actually, it seems like we always
* add predicates if they exist, so that can simply be done in the main
* loop for any accept state creation or modification request.
*
* cover these cases:
* dead end
* single alt
* single alt + preds
* conflict
* conflict + preds
*
* TODO: greedy + those
*/
execATN(dfa, input, startIndex, initialState) {
if (ParserATNSimulator.debug) {
console.log("execATN decision " + dfa.decision + " exec LA(1)==" + this.getLookaheadName(input));
}
let outerContext = initialState.outerContext;
let useContext = initialState.useContext;
let t = input.LA(1);
let previous = initialState;
let contextCache = new PredictionContextCache_1.PredictionContextCache();
while (true) { // while more work
let nextState = this.computeReachSet(dfa, previous, t, contextCache);
if (nextState == null) {
this.setDFAEdge(previous.s0, input.LA(1), ATNSimulator_1.ATNSimulator.ERROR);
return this.handleNoViableAlt(input, startIndex, previous);
}
let D = nextState.s0;
// predicted alt => accept state
assert(D.isAcceptState || D.prediction === ATN_1.ATN.INVALID_ALT_NUMBER);
// conflicted => accept state
assert(D.isAcceptState || D.configs.conflictInfo == null);
if (this.isAcceptState(D, useContext)) {
let conflictingAlts = D.configs.conflictingAlts;
let predictedAlt = conflictingAlts == null ? D.prediction : ATN_1.ATN.INVALID_ALT_NUMBER;
if (predictedAlt !== ATN_1.ATN.INVALID_ALT_NUMBER) {
if (this.optimize_ll1
&& input.index === startIndex
&& !dfa.isPrecedenceDfa
&& nextState.outerContext === nextState.remainingOuterContext
&& dfa.decision >= 0
&& !D.configs.hasSemanticContext) {
if (t >= 0 && t <= MAX_SHORT_VALUE) {
let key = ((dfa.decision << 16) >>> 0) + t;
this.atn.LL1Table.set(key, predictedAlt);
}
}
if (useContext && this.always_try_local_context) {
this.reportContextSensitivity(dfa, predictedAlt, nextState, startIndex, input.index);
}
}
predictedAlt = D.prediction;
// int k = input.index - startIndex + 1; // how much input we used
// System.out.println("used k="+k);
let attemptFullContext = conflictingAlts != null && this.userWantsCtxSensitive;
if (attemptFullContext) {
// Only exact conflicts are known to be ambiguous when local
// prediction does not step out of the decision rule.
attemptFullContext = !useContext
&& (D.configs.dipsIntoOuterContext || !D.configs.isExactConflict)
&& (!this.treat_sllk1_conflict_as_ambiguity || input.index !== startIndex);
}
if (D.configs.hasSemanticContext) {
let predPredictions = D.predicates;
if (predPredictions != null) {
let conflictIndex = input.index;
if (conflictIndex !== startIndex) {
input.seek(startIndex);
}
// use complete evaluation here if we'll want to retry with full context if still ambiguous
conflictingAlts = this.evalSemanticContext(predPredictions, outerContext, attemptFullContext || this.reportAmbiguities);
switch (conflictingAlts.cardinality()) {
case 0:
throw this.noViableAlt(input, outerContext, D.configs, startIndex);
case 1:
return conflictingAlts.nextSetBit(0);
default:
break;
}
if (conflictIndex !== startIndex) {
// restore the index so reporting the fallback to full
// context occurs with the index at the correct spot
input.seek(conflictIndex);
}
}
}
if (!attemptFullContext) {
if (conflictingAlts != null) {
if (this.reportAmbiguities && conflictingAlts.cardinality() > 1) {
this.reportAmbiguity(dfa, D, startIndex, input.index, D.configs.isExactConflict, conflictingAlts, D.configs);
}
predictedAlt = conflictingAlts.nextSetBit(0);
}
return predictedAlt;
}
else {
assert(!useContext);
assert(this.isAcceptState(D, false));
if (ParserATNSimulator.debug) {
console.log("RETRY with outerContext=" + outerContext);
}
let fullContextState = this.computeStartState(dfa, outerContext, true);
if (this.reportAmbiguities) {
this.reportAttemptingFullContext(dfa, conflictingAlts, nextState, startIndex, input.index);
}
input.seek(startIndex);
return this.execATN(dfa, input, startIndex, fullContextState);
}
}
previous = nextState;
if (t !== IntStream_1.IntStream.EOF) {
input.consume();
t = input.LA(1);
}
}
}
/**
* This method is used to improve the localization of error messages by
* choosing an alternative rather than throwing a
* {@link NoViableAltException} in particular prediction scenarios where the
* {@link #ERROR} state was reached during ATN simulation.
*
* The default implementation of this method uses the following
* algorithm to identify an ATN configuration which successfully parsed the
* decision entry rule. Choosing such an alternative ensures that the
* {@link ParserRuleContext} returned by the calling rule will be complete
* and valid, and the syntax error will be reported later at a more
* localized location.
*
* * If no configuration in `configs` reached the end of the
* decision rule, return {@link ATN#INVALID_ALT_NUMBER}.
* * If all configurations in `configs` which reached the end of the
* decision rule predict the same alternative, return that alternative.
* * If the configurations in `configs` which reached the end of the
* decision rule predict multiple alternatives (call this *S*),
* choose an alternative in the following order.
*
* 1. Filter the configurations in `configs` to only those
* configurations which remain viable after evaluating semantic predicates.
* If the set of these filtered configurations which also reached the end of
* the decision rule is not empty, return the minimum alternative
* represented in this set.
* 1. Otherwise, choose the minimum alternative in *S*.
*
* In some scenarios, the algorithm described above could predict an
* alternative which will result in a {@link FailedPredicateException} in
* parser. Specifically, this could occur if the *only* configuration
* capable of successfully parsing to the end of the decision rule is
* blocked by a semantic predicate. By choosing this alternative within
* {@link #adaptivePredict} instead of throwing a
* {@link NoViableAltException}, the resulting
* {@link FailedPredicateException} in the parser will identify the specific
* predicate which is preventing the parser from successfully parsing the
* decision rule, which helps developers identify and correct logic errors
* in semantic predicates.
*
* @param input The input {@link TokenStream}
* @param startIndex The start index for the current prediction, which is
* the input index where any semantic context in `configs` should be
* evaluated
* @param previous The ATN simulation state immediately before the
* {@link #ERROR} state was reached
*
* @returns The value to return from {@link #adaptivePredict}, or
* {@link ATN#INVALID_ALT_NUMBER} if a suitable alternative was not
* identified and {@link #adaptivePredict} should report an error instead.
*/
handleNoViableAlt(input, startIndex, previous) {
if (previous.s0 != null) {
let alts = new BitSet_1.BitSet();
let maxAlt = 0;
for (let config of previous.s0.configs) {
if (config.reachesIntoOuterContext || config.state instanceof RuleStopState_1.RuleStopState) {
alts.set(config.alt);
maxAlt = Math.max(maxAlt, config.alt);
}
}
switch (alts.cardinality()) {
case 0:
break;
case 1:
return alts.nextSetBit(0);
default:
if (!previous.s0.configs.hasSemanticContext) {
// configs doesn't contain any predicates, so the predicate
// filtering code below would be pointless
return alts.nextSetBit(0);
}
/*
* Try to find a configuration set that not only dipped into the outer
* context, but also isn't eliminated by a predicate.
*/
let filteredConfigs = new ATNConfigSet_1.ATNConfigSet();
for (let config of previous.s0.configs) {
if (config.reachesIntoOuterContext || config.state instanceof RuleStopState_1.RuleStopState) {
filteredConfigs.add(config);
}
}
/* The following code blocks are adapted from predicateDFAState with
* the following key changes.
*
* 1. The code operates on an ATNConfigSet rather than a DFAState.
* 2. Predicates are collected for all alternatives represented in
* filteredConfigs, rather than restricting the evaluation to
* conflicting and/or unique configurations.
*/
let altToPred = this.getPredsForAmbigAlts(alts, filteredConfigs, maxAlt);
if (altToPred != null) {
let predicates = this.getPredicatePredictions(alts, altToPred);
if (predicates != null) {
let stopIndex = input.index;
try {
input.seek(startIndex);
let filteredAlts = this.evalSemanticContext(predicates, previous.outerContext, false);
if (!filteredAlts.isEmpty) {
return filteredAlts.nextSetBit(0);
}
}
finally {
input.seek(stopIndex);
}
}
}
return alts.nextSetBit(0);
}
}
throw this.noViableAlt(input, previous.outerContext, previous.s0.configs, startIndex);
}
computeReachSet(dfa, previous, t, contextCache) {
let useContext = previous.useContext;
let remainingGlobalContext = previous.remainingOuterContext;
let s = previous.s0;
if (useContext) {
while (s.isContextSymbol(t)) {
let next;
if (remainingGlobalContext != null) {
remainingGlobalContext = this.skipTailCalls(remainingGlobalContext);
next = s.getContextTarget(this.getReturnState(remainingGlobalContext));
}
if (next == null) {
break;
}
assert(remainingGlobalContext != null);
remainingGlobalContext = remainingGlobalContext.parent;
s = next;
}
}
assert(!this.isAcceptState(s, useContext));
if (this.isAcceptState(s, useContext)) {
return new SimulatorState_1.SimulatorState(previous.outerContext, s, useContext, remainingGlobalContext);
}
let s0 = s;
let target = this.getExistingTargetState(s0, t);
if (target == null) {
let result = this.computeTargetState(dfa, s0, remainingGlobalContext, t, useContext, contextCache);
target = result[0];
remainingGlobalContext = result[1];
}
if (target === ATNSimulator_1.ATNSimulator.ERROR) {
return undefined;
}
assert(!useContext || !target.configs.dipsIntoOuterContext);
return new SimulatorState_1.SimulatorState(previous.outerContext, target, useContext, remainingGlobalContext);
}
/**
* Get an existing target state for an edge in the DFA. If the target state
* for the edge has not yet been computed or is otherwise not available,
* this method returns `undefined`.
*
* @param s The current DFA state
* @param t The next input symbol
* @returns The existing target DFA state for the given input symbol
* `t`, or `undefined` if the target state for this edge is not
* already cached
*/
getExistingTargetState(s, t) {
return s.getTarget(t);
}
/**
* Compute a target state for an edge in the DFA, and attempt to add the
* computed state and corresponding edge to the DFA.
*
* @param dfa
* @param s The current DFA state
* @param remainingGlobalContext
* @param t The next input symbol
* @param useContext
* @param contextCache
*
* @returns The computed target DFA state for the given input symbol
* `t`. If `t` does not lead to a valid DFA state, this method
* returns {@link #ERROR}.
*/
computeTargetState(dfa, s, remainingGlobalContext, t, useContext, contextCache) {
let closureConfigs = s.configs.toArray();
let contextElements;
let reach = new ATNConfigSet_1.ATNConfigSet();
let stepIntoGlobal;
do {
let hasMoreContext = !useContext || remainingGlobalContext != null;
if (!hasMoreContext) {
reach.isOutermostConfigSet = true;
}
let reachIntermediate = new ATNConfigSet_1.ATNConfigSet();
/* Configurations already in a rule stop state indicate reaching the end
* of the decision rule (local context) or end of the start rule (full
* context). Once reached, these configurations are never updated by a
* closure operation, so they are handled separately for the performance
* advantage of having a smaller intermediate set when calling closure.
*
* For full-context reach operations, separate handling is required to
* ensure that the alternative matching the longest overall sequence is
* chosen when multiple such configurations can match the input.
*/
let skippedStopStates;
for (let c of closureConfigs) {
if (ParserATNSimulator.debug) {
console.log("testing " + this.getTokenName(t) + " at " + c.toString());
}
if (c.state instanceof RuleStopState_1.RuleStopState) {
assert(c.context.isEmpty);
if (useContext && !c.reachesIntoOuterContext || t === IntStream_1.IntStream.EOF) {
if (skippedStopStates == null) {
skippedStopStates = [];
}
skippedStopStates.push(c);
}
continue;
}
let n = c.state.numberOfOptimizedTransitions;
for (let ti = 0; ti < n; ti++) { // for each optimized transition
let trans = c.state.getOptimizedTransition(ti);
let target = this.getReachableTarget(c, trans, t);
if (target != null) {
reachIntermediate.add(c.transform(target, false), contextCache);
}
}
}
/* This block optimizes the reach operation for intermediate sets which