From 848c6e43136c08c63c79ca7209fff6b277aa3977 Mon Sep 17 00:00:00 2001 From: parrt Date: Wed, 11 Jan 2012 11:00:05 -0800 Subject: [PATCH] This change is a major restructuring of how left recursive rules are transformed. Previously I simply rewrote the grammar and ANTLR was none the wiser. However, it quickly became apparent that ANTLR needed to do many different things for recursive rules so I had to insert transformation later in the pipeline. Specifically, I needed it after Rule object creation. I made a special LeftRecursiveRule object that track information collected during transformation so that I could use it later. I made major changes to the left recursion templates as well as the little code snippets in Java.stg. I created a new template called LeftRecursiveRuleFunction an accompanying model object that handles the special case, even though there is some duplication. the biggest difference in the grammar is the introduction of => ID notation on any outermost alternative. This information is not added to the tree, instead the ALT node is annotated with the ID. Rule.getAltLabels() now looks also in the LeftRecursiveRuleAltInfo objects. I have moved to the left recursion transformation to its own object and have moved some objects into the analysis package. Further, I have split out the Rule object creation into its own RuleCollector. I renamed discoverAlt in the grammar tree visitor to be discoverOuterAlt an added discoverAlt so we can get information about individual alts even inside subrules. Listeners always get an event for the generic rule context, which is used if there is no specific label for an alternative. Added a list of iteration operations for LL(*) subrules. Split buildRuleFunction into buildLeftRecursiveRuleFunction and one for normal rule function creation. I have to insert lots of extra code to manage the contexts, but of course it's all done using the templates. As long as those templates are correct, this code generation mechanism will work. I removed the st field from the parser rule context. I injected the left recursion transformation inside the SemanticPipeline. Visitor dispatch methods are always added to the generated context structures. Fixed some unit tests. About 24 fail. [git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9854] --- .../Java/src/org/antlr/v4/runtime/Parser.java | 5 +- .../antlr/v4/runtime/ParserRuleContext.java | 34 ++- .../v4/tool/templates/LeftRecursiveRules.stg | 19 +- .../v4/tool/templates/codegen/Java/Java.stg | 39 +++- .../LeftRecursiveRuleAltInfo.java} | 25 ++- .../LeftRecursiveRuleAnalyzer.java | 164 ++++++++------ .../LeftRecursiveRuleTransformer.java | 207 ++++++++++++++++++ .../v4/codegen/OutputModelController.java | 129 +++++++++-- .../org/antlr/v4/codegen/ParserFactory.java | 6 +- .../org/antlr/v4/codegen/model/Action.java | 10 + .../model/LeftRecursiveRuleFunction.java | 60 +++++ .../antlr/v4/codegen/model/ListenerFile.java | 6 +- tool/src/org/antlr/v4/codegen/model/Loop.java | 9 + .../org/antlr/v4/codegen/model/StarBlock.java | 5 - .../model/decl/AltLabelStructDecl.java | 11 +- .../v4/codegen/model/decl/CodeBlock.java | 11 +- .../v4/codegen/model/decl/StructDecl.java | 11 +- .../src/org/antlr/v4/misc/OrderedHashMap.java | 6 +- tool/src/org/antlr/v4/parse/ANTLRParser.g | 4 +- .../org/antlr/v4/parse/BlockSetTransformer.g | 2 +- .../org/antlr/v4/parse/GrammarTreeVisitor.g | 51 ++++- .../antlr/v4/parse/LeftRecursiveRuleWalker.g | 29 ++- .../org/antlr/v4/semantics/RuleCollector.java | 112 ++++++++++ .../antlr/v4/semantics/SemanticPipeline.java | 23 +- .../org/antlr/v4/semantics/SymbolChecks.java | 8 +- .../antlr/v4/semantics/SymbolCollector.java | 47 +--- .../v4/tool/GrammarTransformPipeline.java | 91 +------- .../org/antlr/v4/tool/LeftRecursiveRule.java | 71 ++++++ tool/src/org/antlr/v4/tool/Rule.java | 5 + tool/src/org/antlr/v4/tool/ast/AltAST.java | 1 + .../org/antlr/v4/test/TestASTStructure.gunit | 2 - .../org/antlr/v4/test/TestASTStructure.java | 60 ++--- .../antlr/v4/test/TestATNSerialization.java | 4 +- .../v4/test/TestBasicSemanticErrors.java | 46 +--- .../org/antlr/v4/test/TestLeftRecursion.java | 14 +- 35 files changed, 901 insertions(+), 426 deletions(-) rename tool/src/org/antlr/v4/{codegen/model/LRecursiveRuleFunction.java => analysis/LeftRecursiveRuleAltInfo.java} (70%) rename tool/src/org/antlr/v4/{parse => analysis}/LeftRecursiveRuleAnalyzer.java (68%) create mode 100644 tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java create mode 100644 tool/src/org/antlr/v4/codegen/model/LeftRecursiveRuleFunction.java create mode 100644 tool/src/org/antlr/v4/semantics/RuleCollector.java create mode 100644 tool/src/org/antlr/v4/tool/LeftRecursiveRule.java diff --git a/runtime/Java/src/org/antlr/v4/runtime/Parser.java b/runtime/Java/src/org/antlr/v4/runtime/Parser.java index 6f0580b12..313ee0df6 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/Parser.java +++ b/runtime/Java/src/org/antlr/v4/runtime/Parser.java @@ -463,9 +463,12 @@ public abstract class Parser extends Recognizer getRuleInvocationStack() { + return getRuleInvocationStack(_ctx); + } + + public List getRuleInvocationStack(RuleContext p) { String[] ruleNames = getRuleNames(); List stack = new ArrayList(); - RuleContext p = _ctx; while ( p!=null ) { // compute what follows who invoked us stack.add(ruleNames[p.getRuleIndex()]); diff --git a/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java b/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java index e4b4edb92..c51775dd2 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java @@ -34,7 +34,6 @@ import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.misc.Nullable; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTreeListener; -import org.stringtemplate.v4.ST; import java.util.ArrayList; import java.util.Collections; @@ -102,7 +101,6 @@ public class ParserRuleContext extends RuleContext { public int s = -1; public Symbol start, stop; - public ST st; /** Set during parsing to identify which rule parser is in. */ public int ruleIndex; @@ -112,7 +110,7 @@ public class ParserRuleContext extends RuleContext { public ParserRuleContext() { } - /** COPY a ctx (it deliberately not using copy constructor) */ + /** COPY a ctx (I'm deliberately not using copy constructor) */ public void copyFrom(ParserRuleContext ctx) { // from RuleContext this.parent = ctx.parent; @@ -121,16 +119,15 @@ public class ParserRuleContext extends RuleContext { this.start = ctx.start; this.stop = ctx.stop; - this.st = ctx.st; this.ruleIndex = ctx.ruleIndex; } - public ParserRuleContext(@Nullable ParserRuleContext parent, int invokingStateNumber, int stateNumber) { + public ParserRuleContext(@Nullable ParserRuleContext parent, int invokingStateNumber, int stateNumber) { super(parent, invokingStateNumber); this.s = stateNumber; } - public ParserRuleContext(@Nullable ParserRuleContext parent, int stateNumber) { + public ParserRuleContext(@Nullable ParserRuleContext parent, int stateNumber) { this(parent, parent!=null ? parent.s : -1 /* invoking state */, stateNumber); } @@ -150,7 +147,8 @@ public class ParserRuleContext extends RuleContext { public void enterRule(ParseTreeListener listener) { } public void exitRule(ParseTreeListener listener) { } - public void addChild(TerminalNode t) { + /** Does not set parent link; other add methods do */ + public void addChild(TerminalNode t) { if ( children==null ) children = new ArrayList(); children.add(t); } @@ -175,16 +173,16 @@ public class ParserRuleContext extends RuleContext { // states.add(s); // } - public void addChild(Token matchedToken) { - TerminalNodeImpl t = new TerminalNodeImpl(matchedToken); - t.parent = this; + public void addChild(Symbol matchedToken) { + TerminalNodeImpl t = new TerminalNodeImpl(matchedToken); addChild(t); + t.parent = this; } - public void addErrorNode(Token badToken) { - TerminalNodeImpl t = new ErrorNodeImpl(badToken); - t.parent = this; + public void addErrorNode(Symbol badToken) { + TerminalNodeImpl t = new ErrorNodeImpl(badToken); addChild(t); + t.parent = this; } @Override @@ -198,7 +196,6 @@ public class ParserRuleContext extends RuleContext { @Override public int getRuleIndex() { return ruleIndex; } - public ST getTemplate() { return st; } public Symbol getStart() { return start; } public Symbol getStop() { return stop; } @@ -206,7 +203,7 @@ public class ParserRuleContext extends RuleContext { public String toString(@NotNull Recognizer recog, RuleContext stop) { if ( recog==null ) return super.toString(recog, stop); StringBuilder buf = new StringBuilder(); - ParserRuleContext p = this; + ParserRuleContext p = this; buf.append("["); while ( p != null && p != stop ) { ATN atn = recog.getATN(); @@ -217,21 +214,20 @@ public class ParserRuleContext extends RuleContext { // ATNState invoker = atn.states.get(ctx.invokingState); // RuleTransition rt = (RuleTransition)invoker.transition(0); // buf.append(recog.getRuleNames()[rt.target.ruleIndex]); - p = (ParserRuleContext)p.parent; + p = (ParserRuleContext)p.parent; } buf.append("]"); return buf.toString(); } - /** Used for rule context info debugging during runtime, not so much for ATN debugging */ + /** Used for rule context info debugging during parse-time, not so much for ATN debugging */ public String toInfoString(Parser recognizer) { - List rules = recognizer.getRuleInvocationStack(); + List rules = recognizer.getRuleInvocationStack(this); Collections.reverse(rules); return "ParserRuleContext"+rules+"{" + "altNum=" + altNum + ", start=" + start + ", stop=" + stop + - ", st=" + st + '}'; } } diff --git a/tool/resources/org/antlr/v4/tool/templates/LeftRecursiveRules.stg b/tool/resources/org/antlr/v4/tool/templates/LeftRecursiveRules.stg index ec66dbe21..9ac580e88 100644 --- a/tool/resources/org/antlr/v4/tool/templates/LeftRecursiveRules.stg +++ b/tool/resources/org/antlr/v4/tool/templates/LeftRecursiveRules.stg @@ -37,24 +37,13 @@ recRule(ruleName, precArgDef, argName, primaryAlts, opAlts, setResultAction, userRetvals, leftRecursiveRuleRefLabels) ::= << [] returns [] -options {simrecursion_=true;} - : ( /* */}; separator="\n | "> + : ( }; separator="\n | "> ) - ( options {simrecursion_=true;} - : - - - | {/* (safely) force ANTLR to know about left-recursive rule labels we removed */} - BOGUS_ = BOGUS_}; separator=" "> - + ( )* ; >> -recRuleAlt(alt, startAction, pred) ::= << -{}? -{ - -} - +recRuleAlt(alt, pred) ::= << +{}? >> diff --git a/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg b/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg index 7f8a264b2..f2e957344 100644 --- a/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg +++ b/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg @@ -181,7 +181,9 @@ RuleFunction(currentRule,code,locals,ruleCtx,altLabelCtxs,namedActions,finallyAc } >> -LRecursiveRuleFunction(currentRule,code,locals,ruleCtx,altLabelCtxs,namedActions,finallyAction,postamble) ::= << +LeftRecursiveRuleFunction(currentRule,code,locals,ruleCtx,altLabelCtxs, + namedActions,finallyAction,postamble) ::= +<< @@ -195,7 +197,7 @@ LRecursiveRuleFunction(currentRule,code,locals,ruleCtx,altLabelCtxs,namedActions try { - + _localctx.stop = _input.LT(-1); @@ -348,16 +350,13 @@ case : // TODO: we we need uniqueID? a single _alt might work -StarBlock(choice, alts, sync) ::= << +StarBlock(choice, alts, sync, iteration) ::= << setState(); _errHandler.sync(this); int _alt = getInterpreter().adaptivePredict(_input,,_ctx); while ( _alt!= && _alt!=-1 ) { if ( _alt==1 ) { - - if ( _parseListeners!=null ) triggerExitRuleEvent(); - _prevctx = _localctx; - + } setState(); @@ -501,7 +500,8 @@ ListLabelName(label) ::= "