/** * 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 (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); }
protected int getTokenType(GrammarAST atom) { int ttype; if (g.isLexer()) { ttype = CharSupport.getCharValueFromGrammarCharLiteral(atom.getText()); } else { ttype = g.getTokenType(atom.getText()); } return ttype; }
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 A|B|..|Z alternative block build * * <p>o->o-A->o->o (last ATNState is BlockEndState pointed to by all alts) | ^ |->o-B->o--| | | * ... | | | |->o-Z->o--| * * <p>So start node points at every alternative with epsilon transition and every alt right side * points at a block end ATNState. * * <p>Special case: only one alternative: don't make a block with alt begin/end. * * <p>Special case: if just a list of tokens/chars/sets, then collapse to a single edge'd o-set->o * graph. * * <p>TODO: Set alt number (1..n) in the states? */ public Handle block(BlockAST blkAST, GrammarAST ebnfRoot, List<Handle> alts) { if (ebnfRoot == null) { if (alts.size() == 1) { Handle h = alts.get(0); blkAST.atnState = h.left; return h; } BlockStartState start = newState(BlockStartState.class, blkAST); if (alts.size() > 1) atn.defineDecisionState(start); return makeBlock(start, blkAST, alts); } switch (ebnfRoot.getType()) { case ANTLRParser.OPTIONAL: BlockStartState start = newState(BlockStartState.class, blkAST); atn.defineDecisionState(start); Handle h = makeBlock(start, blkAST, alts); return optional(ebnfRoot, h); case ANTLRParser.CLOSURE: BlockStartState star = newState(StarBlockStartState.class, ebnfRoot); if (alts.size() > 1) atn.defineDecisionState(star); h = makeBlock(star, blkAST, alts); return star(ebnfRoot, h); case ANTLRParser.POSITIVE_CLOSURE: PlusBlockStartState plus = newState(PlusBlockStartState.class, ebnfRoot); if (alts.size() > 1) atn.defineDecisionState(plus); h = makeBlock(plus, blkAST, alts); return plus(ebnfRoot, h); } return null; }
/** 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); }
/** 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; }
/** * 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); }
/* 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; }
// (BLOCK (ALT .)) or (BLOCK (ALT 'a') (ALT .)) public static boolean blockHasWildcardAlt(@NotNull GrammarAST block) { for (Object alt : block.getChildren()) { if (!(alt instanceof AltAST)) continue; AltAST altAST = (AltAST) alt; if (altAST.getChildCount() == 1) { Tree e = altAST.getChild(0); if (e.getType() == ANTLRParser.WILDCARD) { return true; } } } return false; }
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; }