aboutsummaryrefslogtreecommitdiff
path: root/antlr-3.4/tool/src/main/antlr3/org/antlr/grammar/v3/ActionTranslator.g
diff options
context:
space:
mode:
Diffstat (limited to 'antlr-3.4/tool/src/main/antlr3/org/antlr/grammar/v3/ActionTranslator.g')
-rw-r--r--antlr-3.4/tool/src/main/antlr3/org/antlr/grammar/v3/ActionTranslator.g809
1 files changed, 809 insertions, 0 deletions
diff --git a/antlr-3.4/tool/src/main/antlr3/org/antlr/grammar/v3/ActionTranslator.g b/antlr-3.4/tool/src/main/antlr3/org/antlr/grammar/v3/ActionTranslator.g
new file mode 100644
index 0000000..0aca8b8
--- /dev/null
+++ b/antlr-3.4/tool/src/main/antlr3/org/antlr/grammar/v3/ActionTranslator.g
@@ -0,0 +1,809 @@
+/*
+ [The "BSD license"]
+ Copyright (c) 2010 Terence Parr
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+lexer grammar ActionTranslator;
+options {
+ filter=true; // try all non-fragment rules in order specified
+ // output=template; TODO: can we make tokens return templates somehow?
+}
+
+@header {
+package org.antlr.grammar.v3;
+import org.stringtemplate.v4.ST;
+import org.antlr.runtime.*;
+import org.antlr.tool.*;
+import org.antlr.codegen.*;
+
+import org.antlr.runtime.*;
+import java.util.List;
+import java.util.ArrayList;
+import org.antlr.grammar.v3.ANTLRParser;
+
+}
+
+@members {
+public List chunks = new ArrayList();
+Rule enclosingRule;
+int outerAltNum;
+Grammar grammar;
+CodeGenerator generator;
+Token actionToken;
+
+ public ActionTranslator(CodeGenerator generator,
+ String ruleName,
+ GrammarAST actionAST)
+ {
+ this(new ANTLRStringStream(actionAST.token.getText()));
+ this.generator = generator;
+ this.grammar = generator.grammar;
+ this.enclosingRule = grammar.getLocallyDefinedRule(ruleName);
+ this.actionToken = actionAST.token;
+ this.outerAltNum = actionAST.outerAltNum;
+ }
+
+ public ActionTranslator(CodeGenerator generator,
+ String ruleName,
+ Token actionToken,
+ int outerAltNum)
+ {
+ this(new ANTLRStringStream(actionToken.getText()));
+ this.generator = generator;
+ grammar = generator.grammar;
+ this.enclosingRule = grammar.getRule(ruleName);
+ this.actionToken = actionToken;
+ this.outerAltNum = outerAltNum;
+ }
+
+/** Return a list of strings and ST objects that
+ * represent the translated action.
+ */
+public List translateToChunks() {
+ // System.out.println("###\naction="+action);
+ Token t;
+ do {
+ t = nextToken();
+ } while ( t.getType()!= Token.EOF );
+ return chunks;
+}
+
+public String translate() {
+ List theChunks = translateToChunks();
+ //System.out.println("chunks="+a.chunks);
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < theChunks.size(); i++) {
+ Object o = (Object) theChunks.get(i);
+ if ( o instanceof ST ) buf.append(((ST)o).render());
+ else buf.append(o);
+ }
+ //System.out.println("translated: "+buf.toString());
+ return buf.toString();
+}
+
+public List translateAction(String action) {
+ String rname = null;
+ if ( enclosingRule!=null ) {
+ rname = enclosingRule.name;
+ }
+ ActionTranslator translator =
+ new ActionTranslator(generator,
+ rname,
+ new CommonToken(ANTLRParser.ACTION,action),outerAltNum);
+ return translator.translateToChunks();
+}
+
+public boolean isTokenRefInAlt(String id) {
+ return enclosingRule.getTokenRefsInAlt(id, outerAltNum)!=null;
+}
+public boolean isRuleRefInAlt(String id) {
+ return enclosingRule.getRuleRefsInAlt(id, outerAltNum)!=null;
+}
+public Grammar.LabelElementPair getElementLabel(String id) {
+ return enclosingRule.getLabel(id);
+}
+
+public void checkElementRefUniqueness(String ref, boolean isToken) {
+ List refs = null;
+ if ( isToken ) {
+ refs = enclosingRule.getTokenRefsInAlt(ref, outerAltNum);
+ }
+ else {
+ refs = enclosingRule.getRuleRefsInAlt(ref, outerAltNum);
+ }
+ if ( refs!=null && refs.size()>1 ) {
+ ErrorManager.grammarError(ErrorManager.MSG_NONUNIQUE_REF,
+ grammar,
+ actionToken,
+ ref);
+ }
+}
+
+/** For \$rulelabel.name, return the Attribute found for name. It
+ * will be a predefined property or a return value.
+ */
+public Attribute getRuleLabelAttribute(String ruleName, String attrName) {
+ Rule r = grammar.getRule(ruleName);
+ AttributeScope scope = r.getLocalAttributeScope(attrName);
+ if ( scope!=null && !scope.isParameterScope ) {
+ return scope.getAttribute(attrName);
+ }
+ return null;
+}
+
+AttributeScope resolveDynamicScope(String scopeName) {
+ if ( grammar.getGlobalScope(scopeName)!=null ) {
+ return grammar.getGlobalScope(scopeName);
+ }
+ Rule scopeRule = grammar.getRule(scopeName);
+ if ( scopeRule!=null ) {
+ return scopeRule.ruleScope;
+ }
+ return null; // not a valid dynamic scope
+}
+
+protected ST template(String name) {
+ ST st = generator.getTemplates().getInstanceOf(name);
+ chunks.add(st);
+ return st;
+}
+
+
+}
+
+/** $x.y x is enclosing rule, y is a return value, parameter, or
+ * predefined property.
+ *
+ * r[int i] returns [int j]
+ * : {$r.i, $r.j, $r.start, $r.stop, $r.st, $r.tree}
+ * ;
+ */
+SET_ENCLOSING_RULE_SCOPE_ATTR
+ : '$' x=ID '.' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';'
+ {enclosingRule!=null &&
+ $x.text.equals(enclosingRule.name) &&
+ enclosingRule.getLocalAttributeScope($y.text)!=null}?
+ //{System.out.println("found \$rule.attr");}
+ {
+ ST st = null;
+ AttributeScope scope = enclosingRule.getLocalAttributeScope($y.text);
+ if ( scope.isPredefinedRuleScope ) {
+ if ( $y.text.equals("st") || $y.text.equals("tree") ) {
+ st = template("ruleSetPropertyRef_"+$y.text);
+ grammar.referenceRuleLabelPredefinedAttribute($x.text);
+ st.add("scope", $x.text);
+ st.add("attr", $y.text);
+ st.add("expr", translateAction($expr.text));
+ } else {
+ ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
+ grammar,
+ actionToken,
+ $x.text,
+ $y.text);
+ }
+ }
+ else if ( scope.isPredefinedLexerRuleScope ) {
+ // this is a better message to emit than the previous one...
+ ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
+ grammar,
+ actionToken,
+ $x.text,
+ $y.text);
+ }
+ else if ( scope.isParameterScope ) {
+ st = template("parameterSetAttributeRef");
+ st.add("attr", scope.getAttribute($y.text));
+ st.add("expr", translateAction($expr.text));
+ }
+ else { // must be return value
+ st = template("returnSetAttributeRef");
+ st.add("ruleDescriptor", enclosingRule);
+ st.add("attr", scope.getAttribute($y.text));
+ st.add("expr", translateAction($expr.text));
+ }
+ }
+ ;
+ENCLOSING_RULE_SCOPE_ATTR
+ : '$' x=ID '.' y=ID {enclosingRule!=null &&
+ $x.text.equals(enclosingRule.name) &&
+ enclosingRule.getLocalAttributeScope($y.text)!=null}?
+ //{System.out.println("found \$rule.attr");}
+ {
+ if ( isRuleRefInAlt($x.text) ) {
+ ErrorManager.grammarError(ErrorManager.MSG_RULE_REF_AMBIG_WITH_RULE_IN_ALT,
+ grammar,
+ actionToken,
+ $x.text);
+ }
+ ST st = null;
+ AttributeScope scope = enclosingRule.getLocalAttributeScope($y.text);
+ if ( scope.isPredefinedRuleScope ) {
+ st = template("rulePropertyRef_"+$y.text);
+ grammar.referenceRuleLabelPredefinedAttribute($x.text);
+ st.add("scope", $x.text);
+ st.add("attr", $y.text);
+ }
+ else if ( scope.isPredefinedLexerRuleScope ) {
+ // perhaps not the most precise error message to use, but...
+ ErrorManager.grammarError(ErrorManager.MSG_RULE_HAS_NO_ARGS,
+ grammar,
+ actionToken,
+ $x.text);
+ }
+ else if ( scope.isParameterScope ) {
+ st = template("parameterAttributeRef");
+ st.add("attr", scope.getAttribute($y.text));
+ }
+ else { // must be return value
+ st = template("returnAttributeRef");
+ st.add("ruleDescriptor", enclosingRule);
+ st.add("attr", scope.getAttribute($y.text));
+ }
+ }
+ ;
+
+/** Setting $tokenlabel.attr or $tokenref.attr where attr is predefined property of a token is an error. */
+SET_TOKEN_SCOPE_ATTR
+ : '$' x=ID '.' y=ID WS? '='
+ {enclosingRule!=null && input.LA(1)!='=' &&
+ (enclosingRule.getTokenLabel($x.text)!=null||
+ isTokenRefInAlt($x.text)) &&
+ AttributeScope.tokenScope.getAttribute($y.text)!=null}?
+ //{System.out.println("found \$tokenlabel.attr or \$tokenref.attr");}
+ {
+ ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
+ grammar,
+ actionToken,
+ $x.text,
+ $y.text);
+ }
+ ;
+
+/** $tokenlabel.attr or $tokenref.attr where attr is predefined property of a token.
+ * If in lexer grammar, only translate for strings and tokens (rule refs)
+ */
+TOKEN_SCOPE_ATTR
+ : '$' x=ID '.' y=ID {enclosingRule!=null &&
+ (enclosingRule.getTokenLabel($x.text)!=null||
+ isTokenRefInAlt($x.text)) &&
+ AttributeScope.tokenScope.getAttribute($y.text)!=null &&
+ (grammar.type!=Grammar.LEXER ||
+ getElementLabel($x.text).elementRef.token.getType()==ANTLRParser.TOKEN_REF ||
+ getElementLabel($x.text).elementRef.token.getType()==ANTLRParser.STRING_LITERAL)}?
+ // {System.out.println("found \$tokenlabel.attr or \$tokenref.attr");}
+ {
+ String label = $x.text;
+ if ( enclosingRule.getTokenLabel($x.text)==null ) {
+ // \$tokenref.attr gotta get old label or compute new one
+ checkElementRefUniqueness($x.text, true);
+ label = enclosingRule.getElementLabel($x.text, outerAltNum, generator);
+ if ( label==null ) {
+ ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
+ grammar,
+ actionToken,
+ "\$"+$x.text+"."+$y.text);
+ label = $x.text;
+ }
+ }
+ ST st = template("tokenLabelPropertyRef_"+$y.text);
+ st.add("scope", label);
+ st.add("attr", AttributeScope.tokenScope.getAttribute($y.text));
+ }
+ ;
+
+/** Setting $rulelabel.attr or $ruleref.attr where attr is a predefined property is an error
+ * This must also fail, if we try to access a local attribute's field, like $tree.scope = localObject
+ * That must be handled by LOCAL_ATTR below. ANTLR only concerns itself with the top-level scope
+ * attributes declared in scope {} or parameters, return values and the like.
+ */
+SET_RULE_SCOPE_ATTR
+@init {
+Grammar.LabelElementPair pair=null;
+String refdRuleName=null;
+}
+ : '$' x=ID '.' y=ID WS? '=' {enclosingRule!=null && input.LA(1)!='='}?
+ {
+ pair = enclosingRule.getRuleLabel($x.text);
+ refdRuleName = $x.text;
+ if ( pair!=null ) {
+ refdRuleName = pair.referencedRuleName;
+ }
+ }
+ // supercomplicated because I can't exec the above action.
+ // This asserts that if it's a label or a ref to a rule proceed but only if the attribute
+ // is valid for that rule's scope
+ {(enclosingRule.getRuleLabel($x.text)!=null || isRuleRefInAlt($x.text)) &&
+ getRuleLabelAttribute(enclosingRule.getRuleLabel($x.text)!=null?enclosingRule.getRuleLabel($x.text).referencedRuleName:$x.text,$y.text)!=null}?
+ //{System.out.println("found set \$rulelabel.attr or \$ruleref.attr: "+$x.text+"."+$y.text);}
+ {
+ ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
+ grammar,
+ actionToken,
+ $x.text,
+ $y.text);
+ }
+ ;
+
+/** $rulelabel.attr or $ruleref.attr where attr is a predefined property*/
+RULE_SCOPE_ATTR
+@init {
+Grammar.LabelElementPair pair=null;
+String refdRuleName=null;
+}
+ : '$' x=ID '.' y=ID {enclosingRule!=null}?
+ {
+ pair = enclosingRule.getRuleLabel($x.text);
+ refdRuleName = $x.text;
+ if ( pair!=null ) {
+ refdRuleName = pair.referencedRuleName;
+ }
+ }
+ // supercomplicated because I can't exec the above action.
+ // This asserts that if it's a label or a ref to a rule proceed but only if the attribute
+ // is valid for that rule's scope
+ {(enclosingRule.getRuleLabel($x.text)!=null || isRuleRefInAlt($x.text)) &&
+ getRuleLabelAttribute(enclosingRule.getRuleLabel($x.text)!=null?enclosingRule.getRuleLabel($x.text).referencedRuleName:$x.text,$y.text)!=null}?
+ //{System.out.println("found \$rulelabel.attr or \$ruleref.attr: "+$x.text+"."+$y.text);}
+ {
+ String label = $x.text;
+ if ( pair==null ) {
+ // \$ruleref.attr gotta get old label or compute new one
+ checkElementRefUniqueness($x.text, false);
+ label = enclosingRule.getElementLabel($x.text, outerAltNum, generator);
+ if ( label==null ) {
+ ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
+ grammar,
+ actionToken,
+ "\$"+$x.text+"."+$y.text);
+ label = $x.text;
+ }
+ }
+ ST st;
+ Rule refdRule = grammar.getRule(refdRuleName);
+ AttributeScope scope = refdRule.getLocalAttributeScope($y.text);
+ if ( scope.isPredefinedRuleScope ) {
+ st = template("ruleLabelPropertyRef_"+$y.text);
+ grammar.referenceRuleLabelPredefinedAttribute(refdRuleName);
+ st.add("scope", label);
+ st.add("attr", $y.text);
+ }
+ else if ( scope.isPredefinedLexerRuleScope ) {
+ st = template("lexerRuleLabelPropertyRef_"+$y.text);
+ grammar.referenceRuleLabelPredefinedAttribute(refdRuleName);
+ st.add("scope", label);
+ st.add("attr", $y.text);
+ }
+ else if ( scope.isParameterScope ) {
+ // TODO: error!
+ }
+ else {
+ st = template("ruleLabelRef");
+ st.add("referencedRule", refdRule);
+ st.add("scope", label);
+ st.add("attr", scope.getAttribute($y.text));
+ }
+ }
+ ;
+
+
+/** $label either a token label or token/rule list label like label+=expr */
+LABEL_REF
+ : '$' ID {enclosingRule!=null &&
+ getElementLabel($ID.text)!=null &&
+ enclosingRule.getRuleLabel($ID.text)==null}?
+ // {System.out.println("found \$label");}
+ {
+ ST st;
+ Grammar.LabelElementPair pair = getElementLabel($ID.text);
+ if ( pair.type==Grammar.RULE_LIST_LABEL ||
+ pair.type==Grammar.TOKEN_LIST_LABEL ||
+ pair.type==Grammar.WILDCARD_TREE_LIST_LABEL )
+ {
+ st = template("listLabelRef");
+ }
+ else {
+ st = template("tokenLabelRef");
+ }
+ st.add("label", $ID.text);
+ }
+ ;
+
+/** $tokenref in a non-lexer grammar */
+ISOLATED_TOKEN_REF
+ : '$' ID {grammar.type!=Grammar.LEXER && enclosingRule!=null && isTokenRefInAlt($ID.text)}?
+ //{System.out.println("found \$tokenref");}
+ {
+ String label = enclosingRule.getElementLabel($ID.text, outerAltNum, generator);
+ checkElementRefUniqueness($ID.text, true);
+ if ( label==null ) {
+ ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
+ grammar,
+ actionToken,
+ $ID.text);
+ }
+ else {
+ ST st = template("tokenLabelRef");
+ st.add("label", label);
+ }
+ }
+ ;
+
+/** $lexerruleref from within the lexer */
+ISOLATED_LEXER_RULE_REF
+ : '$' ID {grammar.type==Grammar.LEXER &&
+ enclosingRule!=null &&
+ isRuleRefInAlt($ID.text)}?
+ //{System.out.println("found \$lexerruleref");}
+ {
+ String label = enclosingRule.getElementLabel($ID.text, outerAltNum, generator);
+ checkElementRefUniqueness($ID.text, false);
+ if ( label==null ) {
+ ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
+ grammar,
+ actionToken,
+ $ID.text);
+ }
+ else {
+ ST st = template("lexerRuleLabel");
+ st.add("label", label);
+ }
+ }
+ ;
+
+/** $y return value, parameter, predefined rule property, or token/rule
+ * reference within enclosing rule's outermost alt.
+ * y must be a "local" reference; i.e., it must be referring to
+ * something defined within the enclosing rule.
+ *
+ * r[int i] returns [int j]
+ * : {$i, $j, $start, $stop, $st, $tree}
+ * ;
+ *
+ * TODO: this might get the dynamic scope's elements too.!!!!!!!!!
+ */
+SET_LOCAL_ATTR
+ : '$' ID WS? '=' expr=ATTR_VALUE_EXPR ';' {enclosingRule!=null
+ && enclosingRule.getLocalAttributeScope($ID.text)!=null
+ && !enclosingRule.getLocalAttributeScope($ID.text).isPredefinedLexerRuleScope}?
+ //{System.out.println("found set \$localattr");}
+ {
+ ST st;
+ AttributeScope scope = enclosingRule.getLocalAttributeScope($ID.text);
+ if ( scope.isPredefinedRuleScope ) {
+ if ($ID.text.equals("tree") || $ID.text.equals("st")) {
+ st = template("ruleSetPropertyRef_"+$ID.text);
+ grammar.referenceRuleLabelPredefinedAttribute(enclosingRule.name);
+ st.add("scope", enclosingRule.name);
+ st.add("attr", $ID.text);
+ st.add("expr", translateAction($expr.text));
+ } else {
+ ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
+ grammar,
+ actionToken,
+ $ID.text,
+ "");
+ }
+ }
+ else if ( scope.isParameterScope ) {
+ st = template("parameterSetAttributeRef");
+ st.add("attr", scope.getAttribute($ID.text));
+ st.add("expr", translateAction($expr.text));
+ }
+ else {
+ st = template("returnSetAttributeRef");
+ st.add("ruleDescriptor", enclosingRule);
+ st.add("attr", scope.getAttribute($ID.text));
+ st.add("expr", translateAction($expr.text));
+ }
+ }
+ ;
+LOCAL_ATTR
+ : '$' ID {enclosingRule!=null && enclosingRule.getLocalAttributeScope($ID.text)!=null}?
+ //{System.out.println("found \$localattr");}
+ {
+ ST st;
+ AttributeScope scope = enclosingRule.getLocalAttributeScope($ID.text);
+ if ( scope.isPredefinedRuleScope ) {
+ st = template("rulePropertyRef_"+$ID.text);
+ grammar.referenceRuleLabelPredefinedAttribute(enclosingRule.name);
+ st.add("scope", enclosingRule.name);
+ st.add("attr", $ID.text);
+ }
+ else if ( scope.isPredefinedLexerRuleScope ) {
+ st = template("lexerRulePropertyRef_"+$ID.text);
+ st.add("scope", enclosingRule.name);
+ st.add("attr", $ID.text);
+ }
+ else if ( scope.isParameterScope ) {
+ st = template("parameterAttributeRef");
+ st.add("attr", scope.getAttribute($ID.text));
+ }
+ else {
+ st = template("returnAttributeRef");
+ st.add("ruleDescriptor", enclosingRule);
+ st.add("attr", scope.getAttribute($ID.text));
+ }
+ }
+ ;
+
+/** $x::y the only way to access the attributes within a dynamic scope
+ * regardless of whether or not you are in the defining rule.
+ *
+ * scope Symbols { List names; }
+ * r
+ * scope {int i;}
+ * scope Symbols;
+ * : {$r::i=3;} s {$Symbols::names;}
+ * ;
+ * s : {$r::i; $Symbols::names;}
+ * ;
+ */
+SET_DYNAMIC_SCOPE_ATTR
+ : '$' x=ID '::' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';'
+ {resolveDynamicScope($x.text)!=null &&
+ resolveDynamicScope($x.text).getAttribute($y.text)!=null}?
+ //{System.out.println("found set \$scope::attr "+ $x.text + "::" + $y.text + " to " + $expr.text);}
+ {
+ AttributeScope scope = resolveDynamicScope($x.text);
+ if ( scope!=null ) {
+ ST st = template("scopeSetAttributeRef");
+ st.add("scope", $x.text);
+ st.add("attr", scope.getAttribute($y.text));
+ st.add("expr", translateAction($expr.text));
+ }
+ else {
+ // error: invalid dynamic attribute
+ }
+ }
+ ;
+
+DYNAMIC_SCOPE_ATTR
+ : '$' x=ID '::' y=ID
+ {resolveDynamicScope($x.text)!=null &&
+ resolveDynamicScope($x.text).getAttribute($y.text)!=null}?
+ //{System.out.println("found \$scope::attr "+ $x.text + "::" + $y.text);}
+ {
+ AttributeScope scope = resolveDynamicScope($x.text);
+ if ( scope!=null ) {
+ ST st = template("scopeAttributeRef");
+ st.add("scope", $x.text);
+ st.add("attr", scope.getAttribute($y.text));
+ }
+ else {
+ // error: invalid dynamic attribute
+ }
+ }
+ ;
+
+
+ERROR_SCOPED_XY
+ : '$' x=ID '::' y=ID
+ {
+ chunks.add(getText());
+ generator.issueInvalidScopeError($x.text,$y.text,
+ enclosingRule,actionToken,
+ outerAltNum);
+ }
+ ;
+
+/** To access deeper (than top of stack) scopes, use the notation:
+ *
+ * $x[-1]::y previous (just under top of stack)
+ * $x[-i]::y top of stack - i where the '-' MUST BE PRESENT;
+ * i.e., i cannot simply be negative without the '-' sign!
+ * $x[i]::y absolute index i (0..size-1)
+ * $x[0]::y is the absolute 0 indexed element (bottom of the stack)
+ */
+DYNAMIC_NEGATIVE_INDEXED_SCOPE_ATTR
+ : '$' x=ID '[' '-' expr=SCOPE_INDEX_EXPR ']' '::' y=ID
+ // {System.out.println("found \$scope[-...]::attr");}
+ {
+ ST st = template("scopeAttributeRef");
+ st.add("scope", $x.text);
+ st.add("attr", resolveDynamicScope($x.text).getAttribute($y.text));
+ st.add("negIndex", $expr.text);
+ }
+ ;
+
+DYNAMIC_ABSOLUTE_INDEXED_SCOPE_ATTR
+ : '$' x=ID '[' expr=SCOPE_INDEX_EXPR ']' '::' y=ID
+ // {System.out.println("found \$scope[...]::attr");}
+ {
+ ST st = template("scopeAttributeRef");
+ st.add("scope", $x.text);
+ st.add("attr", resolveDynamicScope($x.text).getAttribute($y.text));
+ st.add("index", $expr.text);
+ }
+ ;
+
+fragment
+SCOPE_INDEX_EXPR
+ : (~']')+
+ ;
+
+/** $r y is a rule's dynamic scope or a global shared scope.
+ * Isolated $rulename is not allowed unless it has a dynamic scope *and*
+ * there is no reference to rulename in the enclosing alternative,
+ * which would be ambiguous. See TestAttributes.testAmbiguousRuleRef()
+ */
+ISOLATED_DYNAMIC_SCOPE
+ : '$' ID {resolveDynamicScope($ID.text)!=null}?
+ // {System.out.println("found isolated \$scope where scope is a dynamic scope");}
+ {
+ ST st = template("isolatedDynamicScopeRef");
+ st.add("scope", $ID.text);
+ }
+ ;
+
+// antlr.g then codegen.g does these first two currently.
+// don't want to duplicate that code.
+
+/** %foo(a={},b={},...) ctor */
+TEMPLATE_INSTANCE
+ : '%' ID '(' ( WS? ARG (',' WS? ARG)* WS? )? ')'
+ // {System.out.println("found \%foo(args)");}
+ {
+ String action = getText().substring(1,getText().length());
+ String ruleName = "<outside-of-rule>";
+ if ( enclosingRule!=null ) {
+ ruleName = enclosingRule.name;
+ }
+ ST st =
+ generator.translateTemplateConstructor(ruleName,
+ outerAltNum,
+ actionToken,
+ action);
+ if ( st!=null ) {
+ chunks.add(st);
+ }
+ }
+ ;
+
+/** %({name-expr})(a={},...) indirect template ctor reference */
+INDIRECT_TEMPLATE_INSTANCE
+ : '%' '(' ACTION ')' '(' ( WS? ARG (',' WS? ARG)* WS? )? ')'
+ // {System.out.println("found \%({...})(args)");}
+ {
+ String action = getText().substring(1,getText().length());
+ ST st =
+ generator.translateTemplateConstructor(enclosingRule.name,
+ outerAltNum,
+ actionToken,
+ action);
+ chunks.add(st);
+ }
+ ;
+
+fragment
+ARG : ID '=' ACTION
+ ;
+
+/** %{expr}.y = z; template attribute y of ST-typed expr to z */
+SET_EXPR_ATTRIBUTE
+ : '%' a=ACTION '.' ID WS? '=' expr=ATTR_VALUE_EXPR ';'
+ // {System.out.println("found \%{expr}.y = z;");}
+ {
+ ST st = template("actionSetAttribute");
+ String action = $a.text;
+ action = action.substring(1,action.length()-1); // stuff inside {...}
+ st.add("st", translateAction(action));
+ st.add("attrName", $ID.text);
+ st.add("expr", translateAction($expr.text));
+ }
+ ;
+
+/* %x.y = z; set template attribute y of x (always set never get attr)
+ * to z [languages like python without ';' must still use the
+ * ';' which the code generator is free to remove during code gen]
+ */
+SET_ATTRIBUTE
+ : '%' x=ID '.' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';'
+ // {System.out.println("found \%x.y = z;");}
+ {
+ ST st = template("actionSetAttribute");
+ st.add("st", $x.text);
+ st.add("attrName", $y.text);
+ st.add("expr", translateAction($expr.text));
+ }
+ ;
+
+/** Don't allow an = as first char to prevent $x == 3; kind of stuff. */
+fragment
+ATTR_VALUE_EXPR
+ : ~'=' (~';')*
+ ;
+
+/** %{string-expr} anonymous template from string expr */
+TEMPLATE_EXPR
+ : '%' a=ACTION
+ // {System.out.println("found \%{expr}");}
+ {
+ ST st = template("actionStringConstructor");
+ String action = $a.text;
+ action = action.substring(1,action.length()-1); // stuff inside {...}
+ st.add("stringExpr", translateAction(action));
+ }
+ ;
+
+fragment
+ACTION
+ : '{' (options {greedy=false;}:.)* '}'
+ ;
+
+ESC : '\\' '$' {chunks.add("\$");}
+ | '\\' '%' {chunks.add("\%");}
+ | '\\' ~('$'|'%') {chunks.add(getText());}
+ ;
+
+ERROR_XY
+ : '$' x=ID '.' y=ID
+ {
+ chunks.add(getText());
+ generator.issueInvalidAttributeError($x.text,$y.text,
+ enclosingRule,actionToken,
+ outerAltNum);
+ }
+ ;
+
+ERROR_X
+ : '$' x=ID
+ {
+ chunks.add(getText());
+ generator.issueInvalidAttributeError($x.text,
+ enclosingRule,actionToken,
+ outerAltNum);
+ }
+ ;
+
+UNKNOWN_SYNTAX
+ : '$'
+ {
+ chunks.add(getText());
+ // shouldn't need an error here. Just accept \$ if it doesn't look like anything
+ }
+ | '%' (ID|'.'|'('|')'|','|'{'|'}'|'"')*
+ {
+ chunks.add(getText());
+ ErrorManager.grammarError(ErrorManager.MSG_INVALID_TEMPLATE_ACTION,
+ grammar,
+ actionToken,
+ getText());
+ }
+ ;
+
+TEXT: ~('$'|'%'|'\\')+ {chunks.add(getText());}
+ ;
+
+fragment
+ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
+ ;
+
+fragment
+INT : '0'..'9'+
+ ;
+
+fragment
+WS : (' '|'\t'|'\n'|'\r')+
+ ;