/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.antlr.works.editor.antlr4.completion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNConfig;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.atn.AtomTransition;
import org.antlr.v4.runtime.atn.NotSetTransition;
import org.antlr.v4.runtime.atn.ParserATNSimulator;
import org.antlr.v4.runtime.atn.PredictionContextCache;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.atn.RangeTransition;
import org.antlr.v4.runtime.atn.SetTransition;
import org.antlr.v4.runtime.atn.SimulatorState;
import org.antlr.v4.runtime.atn.Transition;
import org.antlr.v4.runtime.atn.WildcardTransition;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.dfa.DFAState;
import org.antlr.v4.runtime.misc.IntegerList;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.netbeans.api.annotations.common.NonNull;
import org.openide.util.Parameters;
/**
*
* @author Sam Harwell
*/
public abstract class AbstractCompletionParserATNSimulator extends ParserATNSimulator {
private Map<ATNConfig, List<Transition>> caretTransitions;
private CaretToken caretToken;
private List<MultipleDecisionData> decisionPoints;
private IntegerList selections;
private int _firstDecisionIndex;
// state variables used for the custom implementation
private TokenStream _input;
private int _startIndex;
private ParserRuleContext _outerContext;
/** Avoid throwing an exception when the caret is found while computing the start state. */
private boolean _computingStartState;
public AbstractCompletionParserATNSimulator(@NonNull Parser parser, ATN atn) {
super(parser, atn);
Parameters.notNull("parser", parser);
setPredictionMode(PredictionMode.SLL);
}
public Map<ATNConfig, List<Transition>> getCaretTransitions() {
return caretTransitions;
}
public CaretToken getCaretToken() {
return caretToken;
}
public Parser getParser() {
return parser;
}
public void setFixedDecisions(List<MultipleDecisionData> decisionPoints, IntegerList selections) {
Parameters.notNull("decisionPoints", decisionPoints);
Parameters.notNull("selections", selections);
this.decisionPoints = decisionPoints;
this.selections = selections;
this.caretTransitions = null;
if (decisionPoints != null && !decisionPoints.isEmpty()) {
_firstDecisionIndex = decisionPoints.get(0).inputIndex;
} else {
_firstDecisionIndex = Integer.MAX_VALUE;
}
}
@Override
public int adaptivePredict(TokenStream input, int decision, ParserRuleContext outerContext) {
_input = input;
_startIndex = input.index();
_outerContext = outerContext;
caretTransitions = null;
if (decisionPoints != null && _firstDecisionIndex <= _startIndex) {
int index = input.index();
for (int i = 0; i < decisionPoints.size(); i++) {
if (decisionPoints.get(i).inputIndex == index && decisionPoints.get(i).decision == decision) {
return decisionPoints.get(i).alternatives[selections.get(i)];
}
}
}
return super.adaptivePredict(input, decision, outerContext);
}
@Override
public SimulatorState computeStartState(DFA dfa, ParserRuleContext globalContext, boolean useContext) {
_computingStartState = true;
try {
return super.computeStartState(dfa, globalContext, useContext);
} finally {
_computingStartState = false;
}
}
@Override
protected DFAState createDFAState(DFA dfa, ATNConfigSet configs) {
int t = _input.LA(1);
if (t == CaretToken.CARET_TOKEN_TYPE && !_computingStartState) {
caretToken = (CaretToken)_input.LT(1);
throw noViableAlt(_input, _outerContext, configs, _startIndex);
}
return super.createDFAState(dfa, configs);
}
@Override
protected void closure(ATNConfigSet sourceConfigs, ATNConfigSet configs, boolean collectPredicates, boolean hasMoreContext, PredictionContextCache contextCache, boolean treatEofAsEpsilon) {
super.closure(sourceConfigs, configs, collectPredicates, hasMoreContext, contextCache, treatEofAsEpsilon);
}
protected abstract IntervalSet getWordlikeTokenTypes();
@Override
public ATNState getReachableTarget(ATNConfig source, Transition trans, int ttype) {
if (ttype == CaretToken.CARET_TOKEN_TYPE) {
ATNState target = null;
if (trans instanceof AtomTransition) {
AtomTransition at = (AtomTransition)trans;
if (getWordlikeTokenTypes().contains(at.label)) {
target = at.target;
}
} else if (trans instanceof SetTransition) {
SetTransition st = (SetTransition)trans;
boolean not = trans instanceof NotSetTransition;
// TODO: this could probably be done with an intersects method?
for (int t : getWordlikeTokenTypes().toArray()) {
if (!not && st.set.contains(t) || not && !st.set.contains(t)) {
target = st.target;
break;
}
}
} else if (trans instanceof RangeTransition) {
RangeTransition rt = (RangeTransition)trans;
// TODO: there must be a better algorithm here :)
int[] wordlikeTokenTypes = getWordlikeTokenTypes().toArray();
int lb = Arrays.binarySearch(wordlikeTokenTypes, rt.from);
int ub = Arrays.binarySearch(wordlikeTokenTypes, rt.to);
if (lb >= 0 || ub >= 0 || lb != ub) {
target = rt.target;
}
} else if (trans instanceof WildcardTransition) {
target = trans.target;
}
if (caretTransitions == null) {
caretTransitions = new LinkedHashMap<>();
}
List<Transition> configTransitions = caretTransitions.get(source);
if (configTransitions == null) {
configTransitions = new ArrayList<>();
caretTransitions.put(source, configTransitions);
}
configTransitions.add(trans);
return target;
}
return super.getReachableTarget(source, trans, ttype);
}
}