/*
* 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.tvl.goworks.editor.go.parser;
import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.DefaultErrorStrategy;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.atn.ParserATNSimulator;
import org.antlr.v4.runtime.atn.PredicateTransition;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.atn.SemanticContext;
import org.antlr.v4.runtime.atn.Transition;
import org.antlr.works.editor.antlr4.completion.CaretToken;
import org.antlr.works.editor.antlr4.parsing.DescriptiveErrorListener;
import org.netbeans.api.annotations.common.NonNull;
/**
*
* @author Sam Harwell
*/
public class GoParserFactory {
public static final GoParserFactory DEFAULT = new GoParserFactory();
@NonNull
protected GoParser createParser(@NonNull TokenStream input) {
GoParser parser = new GoParserWrapper(input);
return parser;
}
@NonNull
public final GoParser getParser(@NonNull TokenStream input) {
return getParser(input, ParserConfiguration.PRECISE);
}
@NonNull
public GoParser getParser(@NonNull TokenStream input, @NonNull ParserConfiguration configuration) {
GoParser result = createParser(input);
configureParser(result, configuration);
return result;
}
protected void configureParser(@NonNull Parser parser, @NonNull ParserConfiguration configuration) {
ParserATNSimulator interpreter = parser.getInterpreter();
// common configuration
interpreter.force_global_context = false;
interpreter.always_try_local_context = true;
interpreter.optimize_tail_calls = true;
parser.setBuildParseTree(true);
parser.removeErrorListeners();
switch (configuration) {
case FASTEST:
interpreter.setPredictionMode(PredictionMode.SLL);
interpreter.tail_call_preserves_sll = false;
interpreter.treat_sllk1_conflict_as_ambiguity = true;
parser.setErrorHandler(new BailErrorStrategy());
break;
case SLL:
throw new UnsupportedOperationException("The tail_call_preserves_sll flag cannot change within a single ATN instance.");
//interpreter.setPredictionMode(PredictionMode.SLL);
//interpreter.tail_call_preserves_sll = true;
//interpreter.treat_sllk1_conflict_as_ambiguity = true;
//parser.setErrorHandler(new BailErrorStrategy<Token>());
//break;
case HYBRID:
interpreter.setPredictionMode(PredictionMode.LL);
interpreter.tail_call_preserves_sll = false;
interpreter.treat_sllk1_conflict_as_ambiguity = true;
parser.setErrorHandler(new BailErrorStrategy());
break;
case HYBRID_SLL:
throw new UnsupportedOperationException("The tail_call_preserves_sll flag cannot change within a single ATN instance.");
//interpreter.setPredictionMode(PredictionMode.LL);
//interpreter.tail_call_preserves_sll = true;
//interpreter.treat_sllk1_conflict_as_ambiguity = true;
//parser.setErrorHandler(new BailErrorStrategy<Token>());
//break;
case PRECISE:
interpreter.setPredictionMode(PredictionMode.LL);
interpreter.tail_call_preserves_sll = false;
interpreter.treat_sllk1_conflict_as_ambiguity = false;
parser.setErrorHandler(new DefaultErrorStrategy());
parser.addErrorListener(DescriptiveErrorListener.INSTANCE);
break;
default:
throw new IllegalArgumentException("Invalid configuration.");
}
}
public static int getQidDecision(@NonNull ATN atn) {
ATNState decisionState = atn.ruleToStartState[GoParser.RULE_qualifiedIdentifier].transition(0).target;
if (decisionState instanceof DecisionState) {
return ((DecisionState)decisionState).decision;
} else {
return -1;
}
}
public static SemanticContext.Predicate getQidPredicate(@NonNull ATN atn) {
int predicateIndex = -1;
for (ATNState state : atn.states) {
if (state.ruleIndex != GoParser.RULE_qualifiedIdentifier) {
continue;
}
for (int i = 0; i < state.getNumberOfOptimizedTransitions(); i++) {
Transition transition = state.getOptimizedTransition(i);
if (transition instanceof PredicateTransition) {
predicateIndex = ((PredicateTransition)transition).predIndex;
}
}
}
assert predicateIndex >= 0 : "Couldn't find the QID predicate transition.";
return new SemanticContext.Predicate(GoParser.RULE_qualifiedIdentifier, predicateIndex, false);
}
private static final class GoParserWrapper extends GoParser {
public GoParserWrapper(TokenStream input) {
super(input);
_interp = new GoParserATNSimulator(this, _ATN);
}
}
protected static class GoParserATNSimulator extends ParserATNSimulator {
private final SemanticContext.Predicate qidPredicate;
private final int QID_DECISION;
public GoParserATNSimulator(Parser parser, ATN atn) {
super(parser, atn);
QID_DECISION = getQidDecision(atn);
qidPredicate = getQidPredicate(atn);
}
@Override
public int adaptivePredict(TokenStream input, int decision, ParserRuleContext outerContext) {
if (decision == QID_DECISION && QID_DECISION >= 0) {
if (input.LA(1) == GoParser.IDENTIFIER) {
if (input.LA(2) == GoParser.Dot) {
if (input.LA(3) == GoParser.IDENTIFIER) {
return qidPredicate.eval(parser, outerContext) ? 1 : 2;
} else {
assert input.LA(3) != CaretToken.CARET_TOKEN_TYPE;
return 2;
}
} else {
assert input.LA(2) != CaretToken.CARET_TOKEN_TYPE;
return 2;
}
}
}
return super.adaptivePredict(input, decision, outerContext);
}
}
}