| /* |
| [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 { |
| language=Java; |
| 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<Object> chunks = new ArrayList<Object>(); |
| 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<Object> translateToChunks() { |
| // System.out.println("###\naction="+action); |
| Token t; |
| do { |
| t = nextToken(); |
| } while ( t.getType()!= Token.EOF ); |
| return chunks; |
| } |
| |
| public String translate() { |
| List<Object> theChunks = translateToChunks(); |
| //System.out.println("chunks="+a.chunks); |
| StringBuilder buf = new StringBuilder(); |
| for (int i = 0; i < theChunks.size(); i++) { |
| Object o = 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<Object> 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<GrammarAST> 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')+ |
| ; |