package org.antlr.v4.analysis;
import org.antlr.v4.misc.Utils;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.atn.LL1Analyzer;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.tool.ErrorType;
import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.Rule;
import org.antlr.v4.tool.ast.GrammarAST;
import java.util.ArrayList;
import java.util.Arrays;
public class AnalysisPipeline {
public Grammar g;
public AnalysisPipeline(Grammar g) {
this.g = g;
}
public void process() {
LeftRecursionDetector lr = new LeftRecursionDetector(g, g.atn);
lr.check();
if ( !lr.listOfRecursiveCycles.isEmpty() ) return;
if (g.isLexer()) {
processLexer();
}
else {
processParser();
}
}
protected void processLexer() {
for (Rule rule : g.rules.values()) {
if (rule.isFragment()) {
continue;
}
LL1Analyzer analyzer = new LL1Analyzer(g.atn);
IntervalSet look = analyzer.LOOK(g.atn.ruleToStartState[rule.index], null);
if (look.contains(Token.EPSILON)) {
g.tool.errMgr.grammarError(ErrorType.EPSILON_TOKEN, g.fileName, ((GrammarAST)rule.ast.getChild(0)).getToken(), rule.name);
}
}
}
protected void processParser() {
g.decisionLOOK = new ArrayList<IntervalSet[]>(g.atn.getNumberOfDecisions()+1);
for (DecisionState s : g.atn.decisionToState) {
g.tool.log("LL1", "\nDECISION "+s.decision+" in rule "+g.getRule(s.ruleIndex).name);
IntervalSet[] look;
if ( s.nonGreedy ) {
look = new IntervalSet[s.getNumberOfTransitions()+1];
}
else {
LL1Analyzer anal = new LL1Analyzer(g.atn);
look = anal.getDecisionLookahead(s);
g.tool.log("LL1", "look=" + Arrays.toString(look));
}
assert s.decision + 1 >= g.decisionLOOK.size();
Utils.setSize(g.decisionLOOK, s.decision+1);
g.decisionLOOK.set(s.decision, look);
g.tool.log("LL1", "LL(1)? " + disjoint(look));
}
}
public static boolean disjoint(IntervalSet[] altLook) {
boolean collision = false;
IntervalSet combined = new IntervalSet();
if ( altLook==null ) return false;
for (IntervalSet look : altLook) {
if ( look==null ) return false;
if ( !look.and(combined).isNil() ) {
collision = true;
break;
}
combined.addAll(look);
}
return !collision;
}
}