diff options
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.g | 809 |
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')+ + ; |