Got a rule labels added to the tree and also altered code generation so that it pays attention to the labels. fairly major surgery but it's a nice refactoring.

[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9062]
This commit is contained in:
parrt 2011-09-09 17:08:13 -08:00
parent f13fd1973e
commit bf19465437
21 changed files with 197 additions and 49 deletions

View File

@ -57,7 +57,7 @@ public class ParserRuleContext extends RuleContext {
public int ruleIndex;
/** Set during parsing to identify which alt of rule parser is in. */
public int alt;
public int altNum;
public ParserRuleContext() { super(); }

View File

@ -1,10 +1,12 @@
grammar T;
s : i=ifstat {System.out.println(_input.toString(0,_input.index()-1));} ;
ifstat : 'if' '(' INT ')' ID '=' ID ';' ;
ifstat : 'if' '(' INT ')' ID '=' ID ';' # DoIf;
r[int x] returns [int y]
locals [int z]
: name=ID
: name=ID # foo
| ID (ID|';'{;}) # bar
;
EQ : '=' ;

View File

@ -41,9 +41,9 @@ import org.antlr.v4.runtime.tree.ParseTreeListener;
@SuppressWarnings({"all", "warnings", "unchecked", "unused"})
public interface <listener.grammarName>Listener extends ParseTreeListener {
<listener.rules:{r |
void enterRule(<listener.parserName>.<r.name>Context ctx);
void exitRule(<listener.parserName>.<r.name>Context ctx);}; separator="\n">
<listener.listenerNames,listener.ruleNames:{lname,rname |
void enter<lname>(<listener.parserName>.<rname>Context ctx);
void exit<lname>(<listener.parserName>.<rname>Context ctx);}; separator="\n">
}
>>
@ -55,9 +55,9 @@ import org.antlr.v4.runtime.Token;
@SuppressWarnings({"all", "warnings", "unchecked", "unused"})
public class Blank<listener.grammarName>Listener implements <listener.grammarName>Listener {
<listener.rules:{r |
public void enterRule(<listener.parserName>.<r.name>Context ctx) { \}
public void exitRule(<listener.parserName>.<r.name>Context ctx) { \}}; separator="\n">
<listener.listenerNames,listener.ruleNames:{lname,rname |
public void enter<lname>(<listener.parserName>.<rname>Context ctx) { \}
public void exit<lname>(<listener.parserName>.<rname>Context ctx) { \}}; separator="\n">
public void enterEveryRule(ParserRuleContext ctx) { }
public void exitEveryRule(ParserRuleContext ctx) { }
@ -188,6 +188,11 @@ public QStack\<<currentRule.ctxType>\> <currentRule.name>_stk = new QStack\<<cur
}
>>
CodeBlockForOuterMostAlt(c, locals, preamble, ops) ::= <<
_ctx.altNum = <c.altNum>;
<CodeBlockForAlt(...)>
>>
CodeBlockForAlt(c, locals, preamble, ops) ::= <<
{
<locals; separator="\n">
@ -442,7 +447,7 @@ ListLabelName(label) ::= "<label>_list"
CaptureNextToken(d) ::= "<d.varName> = _input.LT(1);"
CaptureNextTokenType(d) ::= "<d.varName> = _input.LA(1);"
StructDecl(s,attrs) ::= <<
StructDecl(s,attrs,visitorDispatchMethods) ::= <<
public static class <s.name> extends ParserRuleContext {
<attrs:{a | public <a>;}; separator="\n">
<if(s.ctorAttrs)>public <s.name>(RuleContext parent, int state) { super(parent, state); }<endif>
@ -450,8 +455,22 @@ public static class <s.name> extends ParserRuleContext {
super(parent, state);
<s.ctorAttrs:{a | this.<a.name> = <a.name>;}; separator="\n">
}
public void enterRule(ParseTreeListener listener) { ((<parser.grammarName>Listener)listener).enterRule(this); }
public void exitRule(ParseTreeListener listener) { ((<parser.grammarName>Listener)listener).exitRule(this); }
<visitorDispatchMethods; separator="\n">
}
>>
VisitorDispatchMethod(method) ::= <<
public void <if(method.isEnter)>enter<else>exit<endif>Rule(ParseTreeListener listener) {
((<parser.grammarName>Listener)listener).<if(method.isEnter)>enter<else>exit<endif><method.listenerName>(this);
}
>>
SwitchedVisitorDispatchMethod(method) ::= <<
public void <if(method.isEnter)>enter<else>exit<endif>Rule(ParseTreeListener listener) {
switch ( altNum ) {
<method.listenerNames:{m |
case <i> : ((<parser.grammarName>Listener)listener).<if(method.isEnter)>enter<else>exit<endif><m>(this); break;}; separator="\n">
}
}
>>

View File

@ -51,7 +51,7 @@ public abstract class BlankOutputModelFactory implements OutputModelFactory {
// ALTERNATIVES / ELEMENTS
public CodeBlockForAlt alternative(Alternative alt) { return null; }
public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) { return null; }
public CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops) { return blk; }

View File

@ -238,7 +238,7 @@ public class OutputModelController {
public CodeGenerator getGenerator() { return delegate.getGenerator(); }
public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) {
CodeBlockForAlt blk = delegate.alternative(alt);
CodeBlockForAlt blk = delegate.alternative(alt, outerMost);
if ( outerMost ) currentOuterMostAlternativeBlock = blk;
for (CodeGeneratorExtension ext : extensions) blk = ext.alternative(blk, outerMost);
return blk;

View File

@ -58,7 +58,7 @@ public interface OutputModelFactory {
// ELEMENT TRIGGERS
CodeBlockForAlt alternative(Alternative alt);
CodeBlockForAlt alternative(Alternative alt, boolean outerMost);
CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops);

View File

@ -34,7 +34,9 @@ import org.antlr.v4.codegen.model.*;
import org.antlr.v4.codegen.model.ast.*;
import org.antlr.v4.codegen.model.decl.*;
import org.antlr.v4.parse.ANTLRParser;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.atn.PlusBlockStartState;
import org.antlr.v4.runtime.atn.StarLoopEntryState;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.semantics.UseDefAnalyzer;
import org.antlr.v4.tool.*;
@ -59,7 +61,10 @@ public class ParserFactory extends DefaultOutputModelFactory {
public CodeBlockForAlt epsilon() { return new CodeBlockForAlt(this); }
public CodeBlockForAlt alternative(Alternative alt) { return new CodeBlockForAlt(this); }
public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) {
if ( outerMost ) return new CodeBlockForOuterMostAlt(this, alt.altNum);
return new CodeBlockForAlt(this);
}
@Override
public CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops) {

View File

@ -0,0 +1,16 @@
package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory;
/** The code associated with an outermost alternative overrule.
* Sometimes we might want to treat them differently in the
* code generation.
*/
public class CodeBlockForOuterMostAlt extends CodeBlockForAlt {
public int altNum;
public CodeBlockForOuterMostAlt(OutputModelFactory factory, int altNum) {
super(factory);
this.altNum = altNum;
}
}

View File

@ -1,11 +1,13 @@
package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.misc.CharSupport;
import org.antlr.v4.tool.ActionAST;
import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.Rule;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
/** A model object representing a parse tree listener file.
* These are the rules specific events triggered by a parse tree visitor.
@ -14,7 +16,8 @@ public class ListenerFile extends OutputModelObject {
public String fileName;
public String grammarName;
public String parserName;
public Collection<Rule> rules;
public List<String> listenerNames = new ArrayList<String>();
public List<String> ruleNames = new ArrayList<String>();
@ModelElement public Action header;
@ -24,7 +27,18 @@ public class ListenerFile extends OutputModelObject {
Grammar g = factory.getGrammar();
parserName = g.getRecognizerName();
grammarName = g.name;
rules = g.rules.values();
for (Rule r : g.rules.values()) {
List<String> labels = r.getAltLabels();
if ( labels==null ) {
listenerNames.add("Rule"); ruleNames.add(r.name);
}
else { // alt(s) with label(s)
for (String label : labels) {
String labelCapitalized = CharSupport.capitalize(label);
listenerNames.add(labelCapitalized); ruleNames.add(r.name);
}
}
}
ActionAST ast = g.namedActions.get("header");
if ( ast!=null ) header = new Action(factory, ast);
}

View File

@ -30,11 +30,14 @@
package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.codegen.model.decl.*;
import org.antlr.v4.codegen.model.decl.Decl;
import org.antlr.v4.codegen.model.decl.StructDecl;
import org.antlr.v4.misc.Utils;
import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.misc.OrderedHashSet;
import org.antlr.v4.tool.*;
import org.antlr.v4.tool.Attribute;
import org.antlr.v4.tool.GrammarAST;
import org.antlr.v4.tool.Rule;
import java.util.*;
@ -72,7 +75,7 @@ public class RuleFunction extends OutputModelObject {
index = r.index;
// might need struct; build but drop later if no elements
ruleCtx = new StructDecl(factory);
ruleCtx = new StructDecl(factory, r);
if ( r.args!=null ) {
ruleCtx.addDecls(r.args.attributes.values());
@ -86,7 +89,7 @@ public class RuleFunction extends OutputModelObject {
ruleCtx.addDecls(r.scope.attributes.values());
}
ruleLabels = r.getLabelNames();
ruleLabels = r.getElementLabelNames();
tokenLabels = r.getTokenRefs();
exceptions = Utils.nodesToStrings(r.exceptionActions);
if ( r.finallyAction!=null ) finallyAction = new Action(factory, r.finallyAction);

View File

@ -0,0 +1,23 @@
package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.misc.CharSupport;
import org.antlr.v4.tool.Rule;
import java.util.ArrayList;
import java.util.List;
public class SwitchedVisitorDispatchMethod extends VisitorDispatchMethod {
public List<String> listenerNames = new ArrayList<String>();
public SwitchedVisitorDispatchMethod(OutputModelFactory factory, Rule r, boolean isEnter) {
super(factory, r, isEnter);
RuleFunction rf = factory.getCurrentRuleFunction();
this.isEnter = isEnter;
List<String> labels = r.getAltLabels();
for (String label : labels) {
String labelCapitalized = CharSupport.capitalize(label);
listenerNames.add(labelCapitalized);
}
}
}

View File

@ -0,0 +1,18 @@
package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.tool.Rule;
import java.util.List;
public class VisitorDispatchMethod extends OutputModelObject {
public String listenerName = "Rule";
public boolean isEnter;
public VisitorDispatchMethod(OutputModelFactory factory, Rule r, boolean isEnter) {
super(factory);
this.isEnter = isEnter;
List<String> label = r.getAltLabels();
if ( label!=null ) listenerName = label.get(0);
}
}

View File

@ -31,22 +31,35 @@ package org.antlr.v4.codegen.model.decl;
import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.codegen.model.ModelElement;
import org.antlr.v4.codegen.model.SwitchedVisitorDispatchMethod;
import org.antlr.v4.codegen.model.VisitorDispatchMethod;
import org.antlr.v4.runtime.misc.OrderedHashSet;
import org.antlr.v4.tool.Attribute;
import org.antlr.v4.tool.Rule;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/** */
public class StructDecl extends Decl {
@ModelElement public OrderedHashSet<Decl> attrs = new OrderedHashSet<Decl>();
@ModelElement public Collection<Attribute> ctorAttrs;
@ModelElement public List<VisitorDispatchMethod> visitorDispatchMethods;
public StructDecl(OutputModelFactory factory) {
public StructDecl(OutputModelFactory factory, Rule r) {
super(factory, null);
}
public StructDecl(OutputModelFactory factory, String name) {
super(factory, name);
List<String> labels = r.getAltLabels();
boolean multiAlts = labels!=null && labels.size()>1;
visitorDispatchMethods = new ArrayList<VisitorDispatchMethod>();
VisitorDispatchMethod enter = multiAlts ?
new SwitchedVisitorDispatchMethod(factory, r, true) :
new VisitorDispatchMethod(factory, r, true);
visitorDispatchMethods.add(enter);
VisitorDispatchMethod exit = multiAlts ?
new SwitchedVisitorDispatchMethod(factory, r, false) :
new VisitorDispatchMethod(factory, r, false);
visitorDispatchMethods.add(exit);
}
public void addDecl(Decl d) { attrs.add(d); }

View File

@ -157,4 +157,7 @@ public class CharSupport {
return buf.toString();
}
public static final String capitalize(String s) {
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
}

View File

@ -448,6 +448,7 @@ ETC : '...' ;
RARROW : '->' ;
TREE_BEGIN : '^(' ;
AT : '@' ;
POUND : '#' ;
NOT : '~' ;
RBRACE : '}' ;

View File

@ -530,10 +530,6 @@ ruleModifier
| FRAGMENT
;
altList
: alternative (OR alternative)* -> alternative+
;
// A set of alts, rewritten as a BLOCK for generic processing
// in tree walkers. Used by the rule 'rule' so that the list of
// alts for a rule appears as a BLOCK containing the alts and
@ -542,13 +538,26 @@ altList
// boundaries set correctly by rule post processing of rewrites.
ruleBlock
@init {Token colon = input.LT(-1);}
: altList -> ^(BLOCK<BlockAST>[colon,"BLOCK"] altList)
: ruleAltList -> ^(BLOCK<BlockAST>[colon,"BLOCK"] ruleAltList)
;
catch [ResyncToEndOfRuleBlock e] {
// just resyncing; ignore error
retval.tree = (GrammarAST)adaptor.errorNode(input, retval.start, input.LT(-1), null);
}
ruleAltList
: labeledAlt (OR labeledAlt)* -> labeledAlt+
;
labeledAlt
: alternative (POUND id {((AltAST)$alternative.tree).altLabel=$id.tree;})?
-> alternative
;
altList
: alternative (OR alternative)* -> alternative+
;
// An individual alt with an optional rewrite clause for the
// elements of the alt.
alternative

View File

@ -29,8 +29,10 @@
package org.antlr.v4.semantics;
import org.antlr.runtime.*;
import org.antlr.v4.parse.*;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.Token;
import org.antlr.v4.parse.ActionSplitter;
import org.antlr.v4.parse.ActionSplitterListener;
import org.antlr.v4.tool.*;
import java.util.List;
@ -204,7 +206,7 @@ public class AttributeChecks implements ActionSplitterListener {
if ( x.equals(r.name) ) return r;
List<LabelElementPair> labels = null;
if ( node.resolver instanceof Rule ) {
labels = r.getLabelDefs().get(x);
labels = r.getElementLabelDefs().get(x);
}
else if ( node.resolver instanceof Alternative ) {
labels = ((Alternative)node.resolver).labelDefs.get(x);

View File

@ -31,7 +31,8 @@ package org.antlr.v4.semantics;
import org.antlr.runtime.Token;
import org.antlr.v4.misc.Utils;
import org.antlr.v4.parse.*;
import org.antlr.v4.parse.ANTLRParser;
import org.antlr.v4.parse.GrammarTreeVisitor;
import org.antlr.v4.tool.*;
import org.stringtemplate.v4.misc.MultiMap;
@ -205,6 +206,7 @@ public class BasicSemanticChecks extends GrammarTreeVisitor {
GrammarAST thrws, GrammarAST options,
List<GrammarAST> actions, GrammarAST block)
{
// TODO: chk that all or no alts have "# label"
checkInvalidRuleDef(ID.token);
}

View File

@ -33,10 +33,15 @@ import org.antlr.runtime.Token;
import org.antlr.runtime.tree.Tree;
import org.antlr.v4.parse.ANTLRParser;
/** An ALT (which can be child of ALT_REWRITE node) */
/** Any ALT (which can be child of ALT_REWRITE node) */
public class AltAST extends GrammarAST {
public Alternative alt;
/** If someone specified an outermost alternative label with #foo.
* Token type will be ID.
*/
public GrammarAST altLabel;
public AltAST(GrammarAST node) {
super(node);
this.alt = ((AltAST)node).alt;

View File

@ -33,16 +33,18 @@ package org.antlr.v4.tool;
import org.antlr.v4.parse.ANTLRParser;
import org.stringtemplate.v4.misc.MultiMap;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
/** Record use/def information about an outermost alternative in a subrule
* or rule of a grammar.
*/
/** An outermost alternative for a rule. We don't track inner alternatives. */
public class Alternative implements AttributeResolver {
public Rule rule;
public AltAST ast;
/** What alternative number is this outermost alt? 1..n */
public int altNum;
// token IDs, string literals in this alt
public MultiMap<String, TerminalAST> tokenRefs = new MultiMap<String, TerminalAST>();
@ -70,7 +72,7 @@ public class Alternative implements AttributeResolver {
*/
public List<ActionAST> actions = new ArrayList<ActionAST>();
public Alternative(Rule r) { this.rule = r; }
public Alternative(Rule r, int altNum) { this.rule = r; this.altNum = altNum; }
/** (ALT_REWRITE (ALT ...) (-> (ALT ...))); rewrite might nested in subrule */
public boolean hasRewrite() {

View File

@ -124,7 +124,7 @@ public class Rule implements AttributeResolver {
this.ast = ast;
this.numberOfAlts = numberOfAlts;
alt = new Alternative[numberOfAlts+1]; // 1..n
for (int i=1; i<=numberOfAlts; i++) alt[i] = new Alternative(this);
for (int i=1; i<=numberOfAlts; i++) alt[i] = new Alternative(this, i);
}
public void defineActionInAlt(int currentAlt, ActionAST actionAST) {
@ -163,7 +163,7 @@ public class Rule implements AttributeResolver {
return refs;
}
public Set<String> getLabelNames() {
public Set<String> getElementLabelNames() {
Set<String> refs = new HashSet<String>();
for (int i=1; i<=numberOfAlts; i++) {
refs.addAll(alt[i].labelDefs.keySet());
@ -172,7 +172,7 @@ public class Rule implements AttributeResolver {
return refs;
}
public MultiMap<String, LabelElementPair> getLabelDefs() {
public MultiMap<String, LabelElementPair> getElementLabelDefs() {
MultiMap<String, LabelElementPair> defs =
new MultiMap<String, LabelElementPair>();
for (int i=1; i<=numberOfAlts; i++) {
@ -185,7 +185,18 @@ public class Rule implements AttributeResolver {
return defs;
}
/** $x Attribute: rule arguments, return values, predefined rule prop.
public List<String> getAltLabels() {
List<String> labels = new ArrayList<String>();
for (int i=1; i<=numberOfAlts; i++) {
GrammarAST altLabel = alt[i].ast.altLabel;
if ( altLabel==null ) break; // all or none
labels.add(altLabel.getText());
}
if ( labels.size()==0 ) return null;
return labels;
}
/** $x Attribute: rule arguments, return values, predefined rule prop.
*/
public Attribute resolveToAttribute(String x, ActionAST node) {
if ( args!=null ) {
@ -253,7 +264,7 @@ public class Rule implements AttributeResolver {
}
public LabelElementPair getAnyLabelDef(String x) {
List<LabelElementPair> labels = getLabelDefs().get(x);
List<LabelElementPair> labels = getElementLabelDefs().get(x);
if ( labels!=null ) return labels.get(0);
return null;
}