/** * From (blk)* build ( blk+ )? with *two* decisions, one for entry and one for choosing alts of * blk. * * <p>|-------------| v | o--[o-blk-o]->o o | ^ -----------------| * * <p>Note that the optional bypass must jump outside the loop as (A|B)* is not the same thing as * (A|B|)+. */ @NotNull public Handle star(@NotNull GrammarAST starAST, @NotNull Handle elem) { StarBlockStartState blkStart = (StarBlockStartState) elem.left; BlockEndState blkEnd = (BlockEndState) elem.right; StarLoopEntryState entry = newState(StarLoopEntryState.class, starAST); atn.defineDecisionState(entry); LoopEndState end = newState(LoopEndState.class, starAST); StarLoopbackState loop = newState(StarLoopbackState.class, starAST); entry.loopBackState = loop; end.loopBackStateNumber = loop.stateNumber; BlockAST blkAST = (BlockAST) starAST.getChild(0); entry.isGreedy = isGreedy(blkAST); if (!g.isLexer() || entry.isGreedy) { epsilon(entry, blkStart); // loop enter edge (alt 1) epsilon(entry, end); // bypass loop edge (alt 2) } else { // only lexers flip entry/exit branches for nongreedy // if not greedy, priority to exit branch; make it first epsilon(entry, end); // bypass loop edge (alt 1) epsilon(entry, blkStart); // loop enter edge (alt 2) } epsilon(blkEnd, loop); // block end hits loop back epsilon(loop, entry); // loop back to entry/exit decision starAST.atnState = entry; // decision is to enter/exit; blk is its own decision return new Handle(entry, end); }
/** From an empty alternative build o-e->o */ public Handle epsilon(GrammarAST node) { ATNState left = newState(node); ATNState right = newState(node); epsilon(left, right); node.atnState = left; return new Handle(left, right); }
/** * From (blk)+ build * * <p>|---------| v | [o-blk-o]->o->o * * <p>We add a decision for loop back node to the existing one at blk start. */ @NotNull public Handle plus(@NotNull GrammarAST plusAST, @NotNull Handle blk) { PlusBlockStartState blkStart = (PlusBlockStartState) blk.left; BlockEndState blkEnd = (BlockEndState) blk.right; PlusLoopbackState loop = newState(PlusLoopbackState.class, plusAST); atn.defineDecisionState(loop); LoopEndState end = newState(LoopEndState.class, plusAST); blkStart.loopBackState = loop; end.loopBackStateNumber = loop.stateNumber; plusAST.atnState = blkStart; epsilon(blkEnd, loop); // blk can see loop back BlockAST blkAST = (BlockAST) plusAST.getChild(0); loop.isGreedy = isGreedy(blkAST); if (!g.isLexer() || loop.isGreedy) { epsilon(loop, blkStart); // loop back to start epsilon(loop, end); // or exit } else { // only lexers flip entry/exit branches for nongreedy // if not greedy, priority to exit branch; make it first epsilon(loop, end); // exit epsilon(loop, blkStart); // loop back to start } return new Handle(blkStart, end); }
/** Build an atom with all possible values in its label */ @NotNull public Handle wildcard(GrammarAST node) { ATNState left = newState(node); ATNState right = newState(node); left.addTransition(new WildcardTransition(right)); node.atnState = left; return new Handle(left, right); }
/** * From (A)? build either: * * <p>o--A->o | ^ o---->| * * <p>or, if A is a block, just add an empty alt to the end of the block */ @NotNull public Handle optional(@NotNull GrammarAST optAST, @NotNull Handle blk) { // TODO: no such thing as nongreedy ()? so give error BlockStartState blkStart = (BlockStartState) blk.left; epsilon(blkStart, blk.right); optAST.atnState = blk.left; return blk; }
/* start->ruleblock->end */ public Handle rule(GrammarAST ruleAST, String name, Handle blk) { Rule r = g.getRule(name); RuleStartState start = atn.ruleToStartState[r.index]; epsilon(start, blk.left); RuleStopState stop = atn.ruleToStopState[r.index]; epsilon(blk.right, stop); Handle h = new Handle(start, stop); // ATNPrinter ser = new ATNPrinter(g, h.left); // System.out.println(ruleAST.toStringTree()+":\n"+ser.asString()); ruleAST.atnState = start; return h; }
public Handle _ruleRef(GrammarAST node) { Rule r = g.getRule(node.getText()); if (r == null) { g.tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "Rule " + node.getText() + " undefined"); return null; } RuleStartState start = atn.ruleToStartState[r.index]; ATNState left = newState(node); ATNState right = newState(node); RuleTransition call = new RuleTransition(start, r.index, right); left.addTransition(call); node.atnState = left; return new Handle(left, right); }
/** * From set build single edge graph o->o-set->o. To conform to what an alt block looks like, must * have extra state on left. This handles ~A also, converted to ~{A} set. */ public Handle set(GrammarAST associatedAST, List<GrammarAST> terminals, boolean invert) { ATNState left = newState(associatedAST); ATNState right = newState(associatedAST); IntervalSet set = new IntervalSet(); for (GrammarAST t : terminals) { int ttype = g.getTokenType(t.getText()); set.add(ttype); } if (invert) { IntervalSet notSet = set.complement(Token.MIN_TOKEN_TYPE, g.getMaxTokenType()); left.addTransition(new NotSetTransition(right, set, notSet)); } else { left.addTransition(new SetTransition(right, set)); } associatedAST.atnState = left; return new Handle(left, right); }
protected Handle makeBlock(BlockStartState start, GrammarAST blkAST, List<Handle> alts) { BlockEndState end = newState(BlockEndState.class, blkAST); start.endState = end; for (Handle alt : alts) { // hook alts up to decision block epsilon(start, alt.left); epsilon(alt.right, end); // no back link in ATN so must walk entire alt to see if we can // strip out the epsilon to 'end' state TailEpsilonRemover opt = new TailEpsilonRemover(); opt.visit(alt.left); } Handle h = new Handle(start, end); // FASerializer ser = new FASerializer(g, h.left); // System.out.println(blkAST.toStringTree()+":\n"+ser); blkAST.atnState = start; return h; }