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; public int ruleIndex;
/** Set during parsing to identify which alt of rule parser is in. */ /** Set during parsing to identify which alt of rule parser is in. */
public int alt; public int altNum;
public ParserRuleContext() { super(); } public ParserRuleContext() { super(); }

View File

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

View File

@ -41,9 +41,9 @@ import org.antlr.v4.runtime.tree.ParseTreeListener;
@SuppressWarnings({"all", "warnings", "unchecked", "unused"}) @SuppressWarnings({"all", "warnings", "unchecked", "unused"})
public interface <listener.grammarName>Listener extends ParseTreeListener { public interface <listener.grammarName>Listener extends ParseTreeListener {
<listener.rules:{r | <listener.listenerNames,listener.ruleNames:{lname,rname |
void enterRule(<listener.parserName>.<r.name>Context ctx); void enter<lname>(<listener.parserName>.<rname>Context ctx);
void exitRule(<listener.parserName>.<r.name>Context ctx);}; separator="\n"> 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"}) @SuppressWarnings({"all", "warnings", "unchecked", "unused"})
public class Blank<listener.grammarName>Listener implements <listener.grammarName>Listener { public class Blank<listener.grammarName>Listener implements <listener.grammarName>Listener {
<listener.rules:{r | <listener.listenerNames,listener.ruleNames:{lname,rname |
public void enterRule(<listener.parserName>.<r.name>Context ctx) { \} public void enter<lname>(<listener.parserName>.<rname>Context ctx) { \}
public void exitRule(<listener.parserName>.<r.name>Context ctx) { \}}; separator="\n"> public void exit<lname>(<listener.parserName>.<rname>Context ctx) { \}}; separator="\n">
public void enterEveryRule(ParserRuleContext ctx) { } public void enterEveryRule(ParserRuleContext ctx) { }
public void exitEveryRule(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) ::= << CodeBlockForAlt(c, locals, preamble, ops) ::= <<
{ {
<locals; separator="\n"> <locals; separator="\n">
@ -442,7 +447,7 @@ ListLabelName(label) ::= "<label>_list"
CaptureNextToken(d) ::= "<d.varName> = _input.LT(1);" CaptureNextToken(d) ::= "<d.varName> = _input.LT(1);"
CaptureNextTokenType(d) ::= "<d.varName> = _input.LA(1);" CaptureNextTokenType(d) ::= "<d.varName> = _input.LA(1);"
StructDecl(s,attrs) ::= << StructDecl(s,attrs,visitorDispatchMethods) ::= <<
public static class <s.name> extends ParserRuleContext { public static class <s.name> extends ParserRuleContext {
<attrs:{a | public <a>;}; separator="\n"> <attrs:{a | public <a>;}; separator="\n">
<if(s.ctorAttrs)>public <s.name>(RuleContext parent, int state) { super(parent, state); }<endif> <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); super(parent, state);
<s.ctorAttrs:{a | this.<a.name> = <a.name>;}; separator="\n"> <s.ctorAttrs:{a | this.<a.name> = <a.name>;}; separator="\n">
} }
public void enterRule(ParseTreeListener listener) { ((<parser.grammarName>Listener)listener).enterRule(this); } <visitorDispatchMethods; separator="\n">
public void exitRule(ParseTreeListener listener) { ((<parser.grammarName>Listener)listener).exitRule(this); } }
>>
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 // 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; } 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 CodeGenerator getGenerator() { return delegate.getGenerator(); }
public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) { public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) {
CodeBlockForAlt blk = delegate.alternative(alt); CodeBlockForAlt blk = delegate.alternative(alt, outerMost);
if ( outerMost ) currentOuterMostAlternativeBlock = blk; if ( outerMost ) currentOuterMostAlternativeBlock = blk;
for (CodeGeneratorExtension ext : extensions) blk = ext.alternative(blk, outerMost); for (CodeGeneratorExtension ext : extensions) blk = ext.alternative(blk, outerMost);
return blk; return blk;

View File

@ -58,7 +58,7 @@ public interface OutputModelFactory {
// ELEMENT TRIGGERS // ELEMENT TRIGGERS
CodeBlockForAlt alternative(Alternative alt); CodeBlockForAlt alternative(Alternative alt, boolean outerMost);
CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops); 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.ast.*;
import org.antlr.v4.codegen.model.decl.*; import org.antlr.v4.codegen.model.decl.*;
import org.antlr.v4.parse.ANTLRParser; 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.runtime.misc.IntervalSet;
import org.antlr.v4.semantics.UseDefAnalyzer; import org.antlr.v4.semantics.UseDefAnalyzer;
import org.antlr.v4.tool.*; import org.antlr.v4.tool.*;
@ -59,7 +61,10 @@ public class ParserFactory extends DefaultOutputModelFactory {
public CodeBlockForAlt epsilon() { return new CodeBlockForAlt(this); } 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 @Override
public CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops) { 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; package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory; import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.misc.CharSupport;
import org.antlr.v4.tool.ActionAST; import org.antlr.v4.tool.ActionAST;
import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.Rule; 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. /** A model object representing a parse tree listener file.
* These are the rules specific events triggered by a parse tree visitor. * 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 fileName;
public String grammarName; public String grammarName;
public String parserName; 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; @ModelElement public Action header;
@ -24,7 +27,18 @@ public class ListenerFile extends OutputModelObject {
Grammar g = factory.getGrammar(); Grammar g = factory.getGrammar();
parserName = g.getRecognizerName(); parserName = g.getRecognizerName();
grammarName = g.name; 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"); ActionAST ast = g.namedActions.get("header");
if ( ast!=null ) header = new Action(factory, ast); if ( ast!=null ) header = new Action(factory, ast);
} }

View File

@ -30,11 +30,14 @@
package org.antlr.v4.codegen.model; package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory; 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.misc.Utils;
import org.antlr.v4.runtime.atn.ATNState; import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.misc.OrderedHashSet; 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.*; import java.util.*;
@ -72,7 +75,7 @@ public class RuleFunction extends OutputModelObject {
index = r.index; index = r.index;
// might need struct; build but drop later if no elements // might need struct; build but drop later if no elements
ruleCtx = new StructDecl(factory); ruleCtx = new StructDecl(factory, r);
if ( r.args!=null ) { if ( r.args!=null ) {
ruleCtx.addDecls(r.args.attributes.values()); ruleCtx.addDecls(r.args.attributes.values());
@ -86,7 +89,7 @@ public class RuleFunction extends OutputModelObject {
ruleCtx.addDecls(r.scope.attributes.values()); ruleCtx.addDecls(r.scope.attributes.values());
} }
ruleLabels = r.getLabelNames(); ruleLabels = r.getElementLabelNames();
tokenLabels = r.getTokenRefs(); tokenLabels = r.getTokenRefs();
exceptions = Utils.nodesToStrings(r.exceptionActions); exceptions = Utils.nodesToStrings(r.exceptionActions);
if ( r.finallyAction!=null ) finallyAction = new Action(factory, r.finallyAction); 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.OutputModelFactory;
import org.antlr.v4.codegen.model.ModelElement; 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.runtime.misc.OrderedHashSet;
import org.antlr.v4.tool.Attribute; import org.antlr.v4.tool.Attribute;
import org.antlr.v4.tool.Rule;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
/** */ /** */
public class StructDecl extends Decl { public class StructDecl extends Decl {
@ModelElement public OrderedHashSet<Decl> attrs = new OrderedHashSet<Decl>(); @ModelElement public OrderedHashSet<Decl> attrs = new OrderedHashSet<Decl>();
@ModelElement public Collection<Attribute> ctorAttrs; @ModelElement public Collection<Attribute> ctorAttrs;
@ModelElement public List<VisitorDispatchMethod> visitorDispatchMethods;
public StructDecl(OutputModelFactory factory) { public StructDecl(OutputModelFactory factory, Rule r) {
super(factory, null); super(factory, null);
} List<String> labels = r.getAltLabels();
boolean multiAlts = labels!=null && labels.size()>1;
public StructDecl(OutputModelFactory factory, String name) { visitorDispatchMethods = new ArrayList<VisitorDispatchMethod>();
super(factory, name); 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); } public void addDecl(Decl d) { attrs.add(d); }

View File

@ -157,4 +157,7 @@ public class CharSupport {
return buf.toString(); 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 : '->' ; RARROW : '->' ;
TREE_BEGIN : '^(' ; TREE_BEGIN : '^(' ;
AT : '@' ; AT : '@' ;
POUND : '#' ;
NOT : '~' ; NOT : '~' ;
RBRACE : '}' ; RBRACE : '}' ;

View File

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

View File

@ -29,8 +29,10 @@
package org.antlr.v4.semantics; package org.antlr.v4.semantics;
import org.antlr.runtime.*; import org.antlr.runtime.ANTLRStringStream;
import org.antlr.v4.parse.*; import org.antlr.runtime.Token;
import org.antlr.v4.parse.ActionSplitter;
import org.antlr.v4.parse.ActionSplitterListener;
import org.antlr.v4.tool.*; import org.antlr.v4.tool.*;
import java.util.List; import java.util.List;
@ -204,7 +206,7 @@ public class AttributeChecks implements ActionSplitterListener {
if ( x.equals(r.name) ) return r; if ( x.equals(r.name) ) return r;
List<LabelElementPair> labels = null; List<LabelElementPair> labels = null;
if ( node.resolver instanceof Rule ) { if ( node.resolver instanceof Rule ) {
labels = r.getLabelDefs().get(x); labels = r.getElementLabelDefs().get(x);
} }
else if ( node.resolver instanceof Alternative ) { else if ( node.resolver instanceof Alternative ) {
labels = ((Alternative)node.resolver).labelDefs.get(x); 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.runtime.Token;
import org.antlr.v4.misc.Utils; 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.antlr.v4.tool.*;
import org.stringtemplate.v4.misc.MultiMap; import org.stringtemplate.v4.misc.MultiMap;
@ -205,6 +206,7 @@ public class BasicSemanticChecks extends GrammarTreeVisitor {
GrammarAST thrws, GrammarAST options, GrammarAST thrws, GrammarAST options,
List<GrammarAST> actions, GrammarAST block) List<GrammarAST> actions, GrammarAST block)
{ {
// TODO: chk that all or no alts have "# label"
checkInvalidRuleDef(ID.token); checkInvalidRuleDef(ID.token);
} }

View File

@ -33,10 +33,15 @@ import org.antlr.runtime.Token;
import org.antlr.runtime.tree.Tree; import org.antlr.runtime.tree.Tree;
import org.antlr.v4.parse.ANTLRParser; 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 class AltAST extends GrammarAST {
public Alternative alt; public Alternative alt;
/** If someone specified an outermost alternative label with #foo.
* Token type will be ID.
*/
public GrammarAST altLabel;
public AltAST(GrammarAST node) { public AltAST(GrammarAST node) {
super(node); super(node);
this.alt = ((AltAST)node).alt; this.alt = ((AltAST)node).alt;

View File

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

View File

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