public String getDOT(ATNState startState, String[] ruleNames, boolean isLexer) {
if ( startState==null ) return null;
// The output DOT graph for visualization
Set<ATNState> markedStates = new HashSet<ATNState>();
ST dot = stlib.getInstanceOf("atn");
dot.add("startState", startState.stateNumber);
dot.add("rankdir", rankdir);
List<ATNState> work = new LinkedList<ATNState>();
work.add(startState);
while ( !work.isEmpty() ) {
ATNState s = work.get(0);
if ( markedStates.contains(s) ) { work.remove(0); continue; }
markedStates.add(s);
// don't go past end of rule node to the follow states
if ( s instanceof RuleStopState) continue;
// special case: if decision point, then line up the alt start states
// unless it's an end of block
// if ( s instanceof BlockStartState ) {
// ST rankST = stlib.getInstanceOf("decision-rank");
// DecisionState alt = (DecisionState)s;
// for (int i=0; i<alt.getNumberOfTransitions(); i++) {
// ATNState target = alt.transition(i).target;
// if ( target!=null ) {
// rankST.add("states", target.stateNumber);
// }
// }
// dot.add("decisionRanks", rankST);
// }
// make a DOT edge for each transition
ST edgeST;
for (int i = 0; i < s.getNumberOfTransitions(); i++) {
Transition edge = s.transition(i);
if ( edge instanceof RuleTransition ) {
RuleTransition rr = ((RuleTransition)edge);
// don't jump to other rules, but display edge to follow node
edgeST = stlib.getInstanceOf("edge");
String label = "<" + ruleNames[rr.ruleIndex];
if (((RuleStartState)rr.target).isPrecedenceRule) {
label += "[" + rr.precedence + "]";
}
label += ">";
edgeST.add("label", label);
edgeST.add("src", "s"+s.stateNumber);
edgeST.add("target", "s"+rr.followState.stateNumber);
edgeST.add("arrowhead", arrowhead);
dot.add("edges", edgeST);
work.add(rr.followState);
continue;
}
if ( edge instanceof ActionTransition) {
edgeST = stlib.getInstanceOf("action-edge");
edgeST.add("label", getEdgeLabel(edge.toString()));
}
else if ( edge instanceof AbstractPredicateTransition ) {
edgeST = stlib.getInstanceOf("edge");
edgeST.add("label", getEdgeLabel(edge.toString()));
}
else if ( edge.isEpsilon() ) {
edgeST = stlib.getInstanceOf("epsilon-edge");
edgeST.add("label", getEdgeLabel(edge.toString()));
boolean loopback = false;
if (edge.target instanceof PlusBlockStartState) {
loopback = s.equals(((PlusBlockStartState)edge.target).loopBackState);
}
else if (edge.target instanceof StarLoopEntryState) {
loopback = s.equals(((StarLoopEntryState)edge.target).loopBackState);
}
edgeST.add("loopback", loopback);
}
else if ( edge instanceof AtomTransition ) {
edgeST = stlib.getInstanceOf("edge");
AtomTransition atom = (AtomTransition)edge;
String label = String.valueOf(atom.label);
if ( isLexer ) label = "'"+getEdgeLabel(String.valueOf((char)atom.label))+"'";
else if ( grammar!=null ) label = grammar.getTokenDisplayName(atom.label);
edgeST.add("label", getEdgeLabel(label));
}
else if ( edge instanceof SetTransition ) {
edgeST = stlib.getInstanceOf("edge");
SetTransition set = (SetTransition)edge;
String label = set.label().toString();
if ( isLexer ) label = set.label().toString(true);
else if ( grammar!=null ) label = set.label().toString(grammar.getVocabulary());
if ( edge instanceof NotSetTransition ) label = "~"+label;
edgeST.add("label", getEdgeLabel(label));
}
else if ( edge instanceof RangeTransition ) {
edgeST = stlib.getInstanceOf("edge");
RangeTransition range = (RangeTransition)edge;
String label = range.label().toString();
if ( isLexer ) label = range.toString();
else if ( grammar!=null ) label = range.label().toString(grammar.getVocabulary());
edgeST.add("label", getEdgeLabel(label));
}
else {
edgeST = stlib.getInstanceOf("edge");
edgeST.add("label", getEdgeLabel(edge.toString()));
}
edgeST.add("src", "s"+s.stateNumber);
edgeST.add("target", "s"+edge.target.stateNumber);
edgeST.add("arrowhead", arrowhead);
if (s.getNumberOfTransitions() > 1) {
edgeST.add("transitionIndex", i);
} else {
edgeST.add("transitionIndex", false);
}
dot.add("edges", edgeST);
work.add(edge.target);
}
}
// define nodes we visited (they will appear first in DOT output)
// this is an example of ST's lazy eval :)
// define stop state first; seems to be a bug in DOT where doublecircle
// shape only works if we define them first. weird.
// ATNState stopState = startState.atn.ruleToStopState.get(startState.rule);
// if ( stopState!=null ) {
// ST st = stlib.getInstanceOf("stopstate");
// st.add("name", "s"+stopState.stateNumber);
// st.add("label", getStateLabel(stopState));
// dot.add("states", st);
// }
for (ATNState s : markedStates) {
if ( !(s instanceof RuleStopState) ) continue;
ST st = stlib.getInstanceOf("stopstate");
st.add("name", "s"+s.stateNumber);
st.add("label", getStateLabel(s));
dot.add("states", st);
}
for (ATNState s : markedStates) {
if ( s instanceof RuleStopState ) continue;
ST st = stlib.getInstanceOf("state");
st.add("name", "s"+s.stateNumber);
st.add("label", getStateLabel(s));
st.add("transitions", s.getTransitions());
dot.add("states", st);
}
return dot.render();
}