implement LR optimisation in JavaScript + align naming + DFAState ctor bugs
This commit is contained in:
parent
6e071f4950
commit
f9d79dfcb2
|
@ -485,7 +485,7 @@ ATNDeserializer.prototype.stateIsEndStateFor = function(state, idx) {
|
|||
|
||||
//
|
||||
// Analyze the {@link StarLoopEntryState} states in the specified ATN to set
|
||||
// the {@link StarLoopEntryState//precedenceRuleDecision} field to the
|
||||
// the {@link StarLoopEntryState//isPrecedenceDecision} field to the
|
||||
// correct value.
|
||||
//
|
||||
// @param atn The ATN.
|
||||
|
@ -505,7 +505,7 @@ ATNDeserializer.prototype.markPrecedenceDecisions = function(atn) {
|
|||
if (maybeLoopEndState instanceof LoopEndState) {
|
||||
if ( maybeLoopEndState.epsilonOnlyTransitions &&
|
||||
(maybeLoopEndState.transitions[0].target instanceof RuleStopState)) {
|
||||
state.precedenceRuleDecision = true;
|
||||
state.isPrecedenceDecision = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -303,7 +303,7 @@ function StarLoopEntryState() {
|
|||
this.stateType = ATNState.STAR_LOOP_ENTRY;
|
||||
this.loopBackState = null;
|
||||
// Indicates whether this state can benefit from a precedence DFA during SLL decision making.
|
||||
this.precedenceRuleDecision = null;
|
||||
this.isPrecedenceDecision = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -262,6 +262,7 @@ var Set = Utils.Set;
|
|||
var BitSet = Utils.BitSet;
|
||||
var DoubleDict = Utils.DoubleDict;
|
||||
var ATN = require('./ATN').ATN;
|
||||
var ATNState = require('./ATNState').ATNState;
|
||||
var ATNConfig = require('./ATNConfig').ATNConfig;
|
||||
var ATNConfigSet = require('./ATNConfigSet').ATNConfigSet;
|
||||
var Token = require('./../Token').Token;
|
||||
|
@ -359,16 +360,7 @@ ParserATNSimulator.prototype.adaptivePredict = function(input, decision, outerCo
|
|||
" exec LA(1)==" + this.getLookaheadName(input) +
|
||||
", outerContext=" + outerContext.toString(this.parser.ruleNames));
|
||||
}
|
||||
// If this is not a precedence DFA, we check the ATN start state
|
||||
// to determine if this ATN start state is the decision for the
|
||||
// closure block that determines whether a precedence rule
|
||||
// should continue or complete.
|
||||
//
|
||||
if (!dfa.precedenceDfa && (dfa.atnStartState instanceof StarLoopEntryState)) {
|
||||
if (dfa.atnStartState.precedenceRuleDecision) {
|
||||
dfa.setPrecedenceDfa(true);
|
||||
}
|
||||
}
|
||||
|
||||
var fullCtx = false;
|
||||
var s0_closure = this.computeStartState(dfa.atnStartState, RuleContext.EMPTY, fullCtx);
|
||||
|
||||
|
@ -379,6 +371,7 @@ ParserATNSimulator.prototype.adaptivePredict = function(input, decision, outerCo
|
|||
// 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 = this.applyPrecedenceFilter(s0_closure);
|
||||
s0 = this.addDFAState(dfa, new DFAState(null, s0_closure));
|
||||
dfa.setPrecedenceStartState(this.parser.getPrecedence(), s0);
|
||||
|
@ -1293,6 +1286,9 @@ ParserATNSimulator.prototype.closure_ = function(config, configs, closureBusy, c
|
|||
// both epsilon transitions and non-epsilon transitions.
|
||||
}
|
||||
for(var i = 0;i<p.transitions.length; i++) {
|
||||
if(i==0 && this.canDropLoopEntryEdgeInLeftRecursiveRule(config))
|
||||
continue;
|
||||
|
||||
var t = p.transitions[i];
|
||||
var continueCollecting = collectPredicates && !(t instanceof ActionTransition);
|
||||
var c = this.getEpsilonTarget(config, t, continueCollecting, depth === 0, fullCtx, treatEofAsEpsilon);
|
||||
|
@ -1337,6 +1333,69 @@ ParserATNSimulator.prototype.closure_ = function(config, configs, closureBusy, c
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
ParserATNSimulator.prototype.canDropLoopEntryEdgeInLeftRecursiveRule = function(config) {
|
||||
// return False
|
||||
var p = config.state;
|
||||
// First check to see if we are in StarLoopEntryState generated during
|
||||
// left-recursion elimination. For efficiency, also check if
|
||||
// the context has an empty stack case. If so, it would mean
|
||||
// global FOLLOW so we can't perform optimization
|
||||
// Are we the special loop entry/exit state? or SLL wildcard
|
||||
if(p.stateType != ATNState.STAR_LOOP_ENTRY || !p.isPrecedenceDecision ||
|
||||
config.context.isEmpty() || config.context.hasEmptyPath())
|
||||
return false;
|
||||
|
||||
// Require all return states to return back to the same rule that p is in.
|
||||
var numCtxs = config.context.length;
|
||||
for(var i=0; i<numCtxs; i++) { // for each stack context
|
||||
var returnState = this.atn.states[config.context.getReturnState(i)];
|
||||
if (returnState.ruleIndex != p.ruleIndex)
|
||||
return false;
|
||||
}
|
||||
|
||||
var decisionStartState = p.transitions[0].target;
|
||||
var blockEndStateNum = decisionStartState.endState.stateNumber;
|
||||
var blockEndState = this.atn.states[blockEndStateNum];
|
||||
|
||||
// Verify that the top of each stack context leads to loop entry/exit
|
||||
// state through epsilon edges and w/o leaving rule.
|
||||
for(var i=0; i<numCtxs; i++) { // for each stack context
|
||||
var returnStateNumber = config.context.getReturnState(i);
|
||||
var returnState = this.atn.states[returnStateNumber];
|
||||
// all states must have single outgoing epsilon edge
|
||||
if (returnState.transitions.length != 1 || !returnState.transitions[0].isEpsilon)
|
||||
return false;
|
||||
|
||||
// Look for prefix op case like 'not expr', (' type ')' expr
|
||||
var returnStateTarget = returnState.transitions[0].target;
|
||||
if ( returnState.stateType == ATNState.BLOCK_END && returnStateTarget == p )
|
||||
continue;
|
||||
|
||||
// Look for 'expr op expr' or case where expr's return state is block end
|
||||
// of (...)* internal block; the block end points to loop back
|
||||
// which points to p but we don't need to check that
|
||||
if ( returnState == blockEndState )
|
||||
continue;
|
||||
|
||||
// Look for ternary expr ? expr : expr. The return state points at block end,
|
||||
// which points at loop entry state
|
||||
if ( returnStateTarget == blockEndState )
|
||||
continue;
|
||||
|
||||
// Look for complex prefix 'between expr and expr' case where 2nd expr's
|
||||
// return state points at block end state of (...)* internal block
|
||||
if (returnStateTarget.stateType == ATNState.BLOCK_END && returnStateTarget.transitionslength == 1
|
||||
&& returnStateTarget.transitions[0].isEpsilon && returnStateTarget.transitions[0].target == p)
|
||||
continue;
|
||||
|
||||
// anything else ain't conforming
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
ParserATNSimulator.prototype.getRuleName = function( index) {
|
||||
if (this.parser!==null && index>=0) {
|
||||
return this.parser.ruleNames[index];
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
var DFAState = require('./DFAState').DFAState;
|
||||
var StarLoopEntryState = require('../atn/ATNState').StarLoopEntryState;
|
||||
var ATNConfigSet = require('./../atn/ATNConfigSet').ATNConfigSet;
|
||||
var DFASerializer = require('./DFASerializer').DFASerializer;
|
||||
var LexerDFASerializer = require('./DFASerializer').LexerDFASerializer;
|
||||
|
@ -58,6 +59,17 @@ function DFA(atnStartState, decision) {
|
|||
// {@code false}. This is the backing field for {@link //isPrecedenceDfa},
|
||||
// {@link //setPrecedenceDfa}.
|
||||
this.precedenceDfa = false;
|
||||
if (atnStartState instanceof StarLoopEntryState)
|
||||
{
|
||||
if (atnStartState.isPrecedenceDecision) {
|
||||
this.precedenceDfa = true;
|
||||
precedenceState = new DFAState(null, new ATNConfigSet());
|
||||
precedenceState.edges = [];
|
||||
precedenceState.isAcceptState = false;
|
||||
precedenceState.requiresFullContext = false;
|
||||
this.s0 = precedenceState;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -125,7 +137,7 @@ DFA.prototype.setPrecedenceDfa = function(precedenceDfa) {
|
|||
if (this.precedenceDfa!==precedenceDfa) {
|
||||
this._states = new DFAStatesSet();
|
||||
if (precedenceDfa) {
|
||||
var precedenceState = new DFAState(new ATNConfigSet());
|
||||
var precedenceState = new DFAState(null, new ATNConfigSet());
|
||||
precedenceState.edges = [];
|
||||
precedenceState.isAcceptState = false;
|
||||
precedenceState.requiresFullContext = false;
|
||||
|
|
Loading…
Reference in New Issue