Home | History | Annotate | Download | only in v3
      1 /*
      2  [The "BSD license"]
      3  Copyright (c) 2010 Terence Parr
      4  All rights reserved.
      5 
      6  Redistribution and use in source and binary forms, with or without
      7  modification, are permitted provided that the following conditions
      8  are met:
      9  1. Redistributions of source code must retain the above copyright
     10     notice, this list of conditions and the following disclaimer.
     11  2. Redistributions in binary form must reproduce the above copyright
     12     notice, this list of conditions and the following disclaimer in the
     13     documentation and/or other materials provided with the distribution.
     14  3. The name of the author may not be used to endorse or promote products
     15     derived from this software without specific prior written permission.
     16 
     17  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 */
     28 lexer grammar ActionTranslator;
     29 options {
     30   filter=true;  // try all non-fragment rules in order specified
     31   // output=template;  TODO: can we make tokens return templates somehow?
     32 }
     33 
     34 @header {
     35 package org.antlr.grammar.v3;
     36 import org.stringtemplate.v4.ST;
     37 import org.antlr.runtime.*;
     38 import org.antlr.tool.*;
     39 import org.antlr.codegen.*;
     40 
     41 import org.antlr.runtime.*;
     42 import java.util.List;
     43 import java.util.ArrayList;
     44 import org.antlr.grammar.v3.ANTLRParser;
     45 
     46 }
     47 
     48 @members {
     49 public List chunks = new ArrayList();
     50 Rule enclosingRule;
     51 int outerAltNum;
     52 Grammar grammar;
     53 CodeGenerator generator;
     54 Token actionToken;
     55 
     56 	public ActionTranslator(CodeGenerator generator,
     57 								 String ruleName,
     58 								 GrammarAST actionAST)
     59 	{
     60 		this(new ANTLRStringStream(actionAST.token.getText()));
     61 		this.generator = generator;
     62 		this.grammar = generator.grammar;
     63 	    this.enclosingRule = grammar.getLocallyDefinedRule(ruleName);
     64 	    this.actionToken = actionAST.token;
     65 	    this.outerAltNum = actionAST.outerAltNum;
     66 	}
     67 
     68 	public ActionTranslator(CodeGenerator generator,
     69 								 String ruleName,
     70 								 Token actionToken,
     71 								 int outerAltNum)
     72 	{
     73 		this(new ANTLRStringStream(actionToken.getText()));
     74 		this.generator = generator;
     75 		grammar = generator.grammar;
     76 	    this.enclosingRule = grammar.getRule(ruleName);
     77 	    this.actionToken = actionToken;
     78 		this.outerAltNum = outerAltNum;
     79 	}
     80 
     81 /** Return a list of strings and ST objects that
     82  *  represent the translated action.
     83  */
     84 public List translateToChunks() {
     85 	// System.out.println("###\naction="+action);
     86 	Token t;
     87 	do {
     88 		t = nextToken();
     89 	} while ( t.getType()!= Token.EOF );
     90 	return chunks;
     91 }
     92 
     93 public String translate() {
     94 	List theChunks = translateToChunks();
     95 	//System.out.println("chunks="+a.chunks);
     96 	StringBuffer buf = new StringBuffer();
     97 	for (int i = 0; i < theChunks.size(); i++) {
     98 		Object o = (Object) theChunks.get(i);
     99 		if ( o instanceof ST ) buf.append(((ST)o).render());
    100 		else buf.append(o);
    101 	}
    102 	//System.out.println("translated: "+buf.toString());
    103 	return buf.toString();
    104 }
    105 
    106 public List translateAction(String action) {
    107 	String rname = null;
    108 	if ( enclosingRule!=null ) {
    109 		rname = enclosingRule.name;
    110 	}
    111 	ActionTranslator translator =
    112 		new ActionTranslator(generator,
    113 								  rname,
    114 								  new CommonToken(ANTLRParser.ACTION,action),outerAltNum);
    115     return translator.translateToChunks();
    116 }
    117 
    118 public boolean isTokenRefInAlt(String id) {
    119     return enclosingRule.getTokenRefsInAlt(id, outerAltNum)!=null;
    120 }
    121 public boolean isRuleRefInAlt(String id) {
    122     return enclosingRule.getRuleRefsInAlt(id, outerAltNum)!=null;
    123 }
    124 public Grammar.LabelElementPair getElementLabel(String id) {
    125     return enclosingRule.getLabel(id);
    126 }
    127 
    128 public void checkElementRefUniqueness(String ref, boolean isToken) {
    129 		List refs = null;
    130 		if ( isToken ) {
    131 		    refs = enclosingRule.getTokenRefsInAlt(ref, outerAltNum);
    132 		}
    133 		else {
    134 		    refs = enclosingRule.getRuleRefsInAlt(ref, outerAltNum);
    135 		}
    136 		if ( refs!=null && refs.size()>1 ) {
    137 			ErrorManager.grammarError(ErrorManager.MSG_NONUNIQUE_REF,
    138 									  grammar,
    139 									  actionToken,
    140 									  ref);
    141 		}
    142 }
    143 
    144 /** For \$rulelabel.name, return the Attribute found for name.  It
    145  *  will be a predefined property or a return value.
    146  */
    147 public Attribute getRuleLabelAttribute(String ruleName, String attrName) {
    148 	Rule r = grammar.getRule(ruleName);
    149 	AttributeScope scope = r.getLocalAttributeScope(attrName);
    150 	if ( scope!=null && !scope.isParameterScope ) {
    151 		return scope.getAttribute(attrName);
    152 	}
    153 	return null;
    154 }
    155 
    156 AttributeScope resolveDynamicScope(String scopeName) {
    157 	if ( grammar.getGlobalScope(scopeName)!=null ) {
    158 		return grammar.getGlobalScope(scopeName);
    159 	}
    160 	Rule scopeRule = grammar.getRule(scopeName);
    161 	if ( scopeRule!=null ) {
    162 		return scopeRule.ruleScope;
    163 	}
    164 	return null; // not a valid dynamic scope
    165 }
    166 
    167 protected ST template(String name) {
    168 	ST st = generator.getTemplates().getInstanceOf(name);
    169 	chunks.add(st);
    170 	return st;
    171 }
    172 
    173 
    174 }
    175 
    176 /**	$x.y	x is enclosing rule, y is a return value, parameter, or
    177  * 			predefined property.
    178  *
    179  * 			r[int i] returns [int j]
    180  * 				:	{$r.i, $r.j, $r.start, $r.stop, $r.st, $r.tree}
    181  * 				;
    182  */
    183 SET_ENCLOSING_RULE_SCOPE_ATTR
    184 	:	'$' x=ID '.' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';'
    185 							{enclosingRule!=null &&
    186 	                         $x.text.equals(enclosingRule.name) &&
    187 	                         enclosingRule.getLocalAttributeScope($y.text)!=null}?
    188 		//{System.out.println("found \$rule.attr");}
    189 		{
    190 		ST st = null;
    191 		AttributeScope scope = enclosingRule.getLocalAttributeScope($y.text);
    192 		if ( scope.isPredefinedRuleScope ) {
    193 			if ( $y.text.equals("st") || $y.text.equals("tree") ) {
    194 				st = template("ruleSetPropertyRef_"+$y.text);
    195 				grammar.referenceRuleLabelPredefinedAttribute($x.text);
    196 				st.add("scope", $x.text);
    197 				st.add("attr", $y.text);
    198 				st.add("expr", translateAction($expr.text));
    199 			} else {
    200 				ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
    201 										  grammar,
    202 										  actionToken,
    203 										  $x.text,
    204 										  $y.text);
    205 			}
    206 		}
    207 	    else if ( scope.isPredefinedLexerRuleScope ) {
    208 	    	// this is a better message to emit than the previous one...
    209 			ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
    210 									  grammar,
    211 									  actionToken,
    212 									  $x.text,
    213 									  $y.text);
    214 	    }
    215 		else if ( scope.isParameterScope ) {
    216 			st = template("parameterSetAttributeRef");
    217 			st.add("attr", scope.getAttribute($y.text));
    218 			st.add("expr", translateAction($expr.text));
    219 		}
    220 		else { // must be return value
    221 			st = template("returnSetAttributeRef");
    222 			st.add("ruleDescriptor", enclosingRule);
    223 			st.add("attr", scope.getAttribute($y.text));
    224 			st.add("expr", translateAction($expr.text));
    225 		}
    226 		}
    227 	;
    228 ENCLOSING_RULE_SCOPE_ATTR
    229 	:	'$' x=ID '.' y=ID	{enclosingRule!=null &&
    230 	                         $x.text.equals(enclosingRule.name) &&
    231 	                         enclosingRule.getLocalAttributeScope($y.text)!=null}?
    232 		//{System.out.println("found \$rule.attr");}
    233 		{
    234 		if ( isRuleRefInAlt($x.text)  ) {
    235 			ErrorManager.grammarError(ErrorManager.MSG_RULE_REF_AMBIG_WITH_RULE_IN_ALT,
    236 									  grammar,
    237 									  actionToken,
    238 									  $x.text);
    239 		}
    240 		ST st = null;
    241 		AttributeScope scope = enclosingRule.getLocalAttributeScope($y.text);
    242 		if ( scope.isPredefinedRuleScope ) {
    243 			st = template("rulePropertyRef_"+$y.text);
    244 			grammar.referenceRuleLabelPredefinedAttribute($x.text);
    245 			st.add("scope", $x.text);
    246 			st.add("attr", $y.text);
    247 		}
    248 	    else if ( scope.isPredefinedLexerRuleScope ) {
    249 	    	// perhaps not the most precise error message to use, but...
    250 			ErrorManager.grammarError(ErrorManager.MSG_RULE_HAS_NO_ARGS,
    251 									  grammar,
    252 									  actionToken,
    253 									  $x.text);
    254 	    }
    255 		else if ( scope.isParameterScope ) {
    256 			st = template("parameterAttributeRef");
    257 			st.add("attr", scope.getAttribute($y.text));
    258 		}
    259 		else { // must be return value
    260 			st = template("returnAttributeRef");
    261 			st.add("ruleDescriptor", enclosingRule);
    262 			st.add("attr", scope.getAttribute($y.text));
    263 		}
    264 		}
    265 	;
    266 
    267 /** Setting $tokenlabel.attr or $tokenref.attr where attr is predefined property of a token is an error. */
    268 SET_TOKEN_SCOPE_ATTR
    269 	:	'$' x=ID '.' y=ID WS? '='
    270 							 {enclosingRule!=null && input.LA(1)!='=' &&
    271 	                         (enclosingRule.getTokenLabel($x.text)!=null||
    272 	                          isTokenRefInAlt($x.text)) &&
    273 	                         AttributeScope.tokenScope.getAttribute($y.text)!=null}?
    274 		//{System.out.println("found \$tokenlabel.attr or \$tokenref.attr");}
    275 		{
    276 		ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
    277 								  grammar,
    278 								  actionToken,
    279 								  $x.text,
    280 								  $y.text);
    281 		}
    282 	;
    283 
    284 /** $tokenlabel.attr or $tokenref.attr where attr is predefined property of a token.
    285  *  If in lexer grammar, only translate for strings and tokens (rule refs)
    286  */
    287 TOKEN_SCOPE_ATTR
    288 	:	'$' x=ID '.' y=ID	{enclosingRule!=null &&
    289 	                         (enclosingRule.getTokenLabel($x.text)!=null||
    290 	                          isTokenRefInAlt($x.text)) &&
    291 	                         AttributeScope.tokenScope.getAttribute($y.text)!=null &&
    292 	                         (grammar.type!=Grammar.LEXER ||
    293 	                         getElementLabel($x.text).elementRef.token.getType()==ANTLRParser.TOKEN_REF ||
    294 	                         getElementLabel($x.text).elementRef.token.getType()==ANTLRParser.STRING_LITERAL)}?
    295 		// {System.out.println("found \$tokenlabel.attr or \$tokenref.attr");}
    296 		{
    297 		String label = $x.text;
    298 		if ( enclosingRule.getTokenLabel($x.text)==null ) {
    299 			// \$tokenref.attr  gotta get old label or compute new one
    300 			checkElementRefUniqueness($x.text, true);
    301 			label = enclosingRule.getElementLabel($x.text, outerAltNum, generator);
    302 			if ( label==null ) {
    303 				ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
    304 										  grammar,
    305 										  actionToken,
    306 										  "\$"+$x.text+"."+$y.text);
    307 				label = $x.text;
    308 			}
    309 		}
    310 		ST st = template("tokenLabelPropertyRef_"+$y.text);
    311 		st.add("scope", label);
    312 		st.add("attr", AttributeScope.tokenScope.getAttribute($y.text));
    313 		}
    314 	;
    315 
    316 /** Setting $rulelabel.attr or $ruleref.attr where attr is a predefined property is an error
    317  *  This must also fail, if we try to access a local attribute's field, like $tree.scope = localObject
    318  *  That must be handled by LOCAL_ATTR below. ANTLR only concerns itself with the top-level scope
    319  *  attributes declared in scope {} or parameters, return values and the like.
    320  */
    321 SET_RULE_SCOPE_ATTR
    322 @init {
    323 Grammar.LabelElementPair pair=null;
    324 String refdRuleName=null;
    325 }
    326 	:	'$' x=ID '.' y=ID WS? '=' {enclosingRule!=null && input.LA(1)!='='}?
    327 		{
    328 		pair = enclosingRule.getRuleLabel($x.text);
    329 		refdRuleName = $x.text;
    330 		if ( pair!=null ) {
    331 			refdRuleName = pair.referencedRuleName;
    332 		}
    333 		}
    334 		// supercomplicated because I can't exec the above action.
    335 		// This asserts that if it's a label or a ref to a rule proceed but only if the attribute
    336 		// is valid for that rule's scope
    337 		{(enclosingRule.getRuleLabel($x.text)!=null || isRuleRefInAlt($x.text)) &&
    338 	      getRuleLabelAttribute(enclosingRule.getRuleLabel($x.text)!=null?enclosingRule.getRuleLabel($x.text).referencedRuleName:$x.text,$y.text)!=null}?
    339 		//{System.out.println("found set \$rulelabel.attr or \$ruleref.attr: "+$x.text+"."+$y.text);}
    340 		{
    341 		ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
    342 								  grammar,
    343 								  actionToken,
    344 								  $x.text,
    345 								  $y.text);
    346 		}
    347 	;
    348 
    349 /** $rulelabel.attr or $ruleref.attr where attr is a predefined property*/
    350 RULE_SCOPE_ATTR
    351 @init {
    352 Grammar.LabelElementPair pair=null;
    353 String refdRuleName=null;
    354 }
    355 	:	'$' x=ID '.' y=ID {enclosingRule!=null}?
    356 		{
    357 		pair = enclosingRule.getRuleLabel($x.text);
    358 		refdRuleName = $x.text;
    359 		if ( pair!=null ) {
    360 			refdRuleName = pair.referencedRuleName;
    361 		}
    362 		}
    363 		// supercomplicated because I can't exec the above action.
    364 		// This asserts that if it's a label or a ref to a rule proceed but only if the attribute
    365 		// is valid for that rule's scope
    366 		{(enclosingRule.getRuleLabel($x.text)!=null || isRuleRefInAlt($x.text)) &&
    367 	      getRuleLabelAttribute(enclosingRule.getRuleLabel($x.text)!=null?enclosingRule.getRuleLabel($x.text).referencedRuleName:$x.text,$y.text)!=null}?
    368 		//{System.out.println("found \$rulelabel.attr or \$ruleref.attr: "+$x.text+"."+$y.text);}
    369 		{
    370 		String label = $x.text;
    371 		if ( pair==null ) {
    372 			// \$ruleref.attr  gotta get old label or compute new one
    373 			checkElementRefUniqueness($x.text, false);
    374 			label = enclosingRule.getElementLabel($x.text, outerAltNum, generator);
    375 			if ( label==null ) {
    376 				ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
    377 										  grammar,
    378 										  actionToken,
    379 										  "\$"+$x.text+"."+$y.text);
    380 				label = $x.text;
    381 			}
    382 		}
    383 		ST st;
    384 		Rule refdRule = grammar.getRule(refdRuleName);
    385 		AttributeScope scope = refdRule.getLocalAttributeScope($y.text);
    386 		if ( scope.isPredefinedRuleScope ) {
    387 			st = template("ruleLabelPropertyRef_"+$y.text);
    388 			grammar.referenceRuleLabelPredefinedAttribute(refdRuleName);
    389 			st.add("scope", label);
    390 			st.add("attr", $y.text);
    391 		}
    392 		else if ( scope.isPredefinedLexerRuleScope ) {
    393 			st = template("lexerRuleLabelPropertyRef_"+$y.text);
    394 			grammar.referenceRuleLabelPredefinedAttribute(refdRuleName);
    395 			st.add("scope", label);
    396 			st.add("attr", $y.text);
    397 		}
    398 		else if ( scope.isParameterScope ) {
    399 			// TODO: error!
    400 		}
    401 		else {
    402 			st = template("ruleLabelRef");
    403 			st.add("referencedRule", refdRule);
    404 			st.add("scope", label);
    405 			st.add("attr", scope.getAttribute($y.text));
    406 		}
    407 		}
    408 	;
    409 
    410 
    411 /** $label	either a token label or token/rule list label like label+=expr */
    412 LABEL_REF
    413 	:	'$' ID {enclosingRule!=null &&
    414 	            getElementLabel($ID.text)!=null &&
    415 		        enclosingRule.getRuleLabel($ID.text)==null}?
    416 		// {System.out.println("found \$label");}
    417 		{
    418 		ST st;
    419 		Grammar.LabelElementPair pair = getElementLabel($ID.text);
    420 		if ( pair.type==Grammar.RULE_LIST_LABEL ||
    421              pair.type==Grammar.TOKEN_LIST_LABEL ||
    422              pair.type==Grammar.WILDCARD_TREE_LIST_LABEL )
    423         {
    424 			st = template("listLabelRef");
    425 		}
    426 		else {
    427 			st = template("tokenLabelRef");
    428 		}
    429 		st.add("label", $ID.text);
    430 		}
    431 	;
    432 
    433 /** $tokenref in a non-lexer grammar */
    434 ISOLATED_TOKEN_REF
    435 	:	'$' ID	{grammar.type!=Grammar.LEXER && enclosingRule!=null && isTokenRefInAlt($ID.text)}?
    436 		//{System.out.println("found \$tokenref");}
    437 		{
    438 		String label = enclosingRule.getElementLabel($ID.text, outerAltNum, generator);
    439 		checkElementRefUniqueness($ID.text, true);
    440 		if ( label==null ) {
    441 			ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
    442 									  grammar,
    443 									  actionToken,
    444 									  $ID.text);
    445 		}
    446 		else {
    447 			ST st = template("tokenLabelRef");
    448 			st.add("label", label);
    449 		}
    450 		}
    451 	;
    452 
    453 /** $lexerruleref from within the lexer */
    454 ISOLATED_LEXER_RULE_REF
    455 	:	'$' ID	{grammar.type==Grammar.LEXER &&
    456 	             enclosingRule!=null &&
    457 	             isRuleRefInAlt($ID.text)}?
    458 		//{System.out.println("found \$lexerruleref");}
    459 		{
    460 		String label = enclosingRule.getElementLabel($ID.text, outerAltNum, generator);
    461 		checkElementRefUniqueness($ID.text, false);
    462 		if ( label==null ) {
    463 			ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF,
    464 									  grammar,
    465 									  actionToken,
    466 									  $ID.text);
    467 		}
    468 		else {
    469 			ST st = template("lexerRuleLabel");
    470 			st.add("label", label);
    471 		}
    472 		}
    473 	;
    474 
    475 /**  $y 	return value, parameter, predefined rule property, or token/rule
    476  *          reference within enclosing rule's outermost alt.
    477  *          y must be a "local" reference; i.e., it must be referring to
    478  *          something defined within the enclosing rule.
    479  *
    480  * 			r[int i] returns [int j]
    481  * 				:	{$i, $j, $start, $stop, $st, $tree}
    482  *              ;
    483  *
    484  *	TODO: this might get the dynamic scope's elements too.!!!!!!!!!
    485  */
    486 SET_LOCAL_ATTR
    487 	:	'$' ID WS? '=' expr=ATTR_VALUE_EXPR ';' {enclosingRule!=null
    488 													&& enclosingRule.getLocalAttributeScope($ID.text)!=null
    489 													&& !enclosingRule.getLocalAttributeScope($ID.text).isPredefinedLexerRuleScope}?
    490 		//{System.out.println("found set \$localattr");}
    491 		{
    492 		ST st;
    493 		AttributeScope scope = enclosingRule.getLocalAttributeScope($ID.text);
    494 		if ( scope.isPredefinedRuleScope ) {
    495 			if ($ID.text.equals("tree") || $ID.text.equals("st")) {
    496 				st = template("ruleSetPropertyRef_"+$ID.text);
    497 				grammar.referenceRuleLabelPredefinedAttribute(enclosingRule.name);
    498 				st.add("scope", enclosingRule.name);
    499 				st.add("attr", $ID.text);
    500 				st.add("expr", translateAction($expr.text));
    501 			} else {
    502 				ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR,
    503 										 grammar,
    504 										 actionToken,
    505 										 $ID.text,
    506 										 "");
    507 			}
    508 		}
    509 		else if ( scope.isParameterScope ) {
    510 			st = template("parameterSetAttributeRef");
    511 			st.add("attr", scope.getAttribute($ID.text));
    512 			st.add("expr", translateAction($expr.text));
    513 		}
    514 		else {
    515 			st = template("returnSetAttributeRef");
    516 			st.add("ruleDescriptor", enclosingRule);
    517 			st.add("attr", scope.getAttribute($ID.text));
    518 			st.add("expr", translateAction($expr.text));
    519 			}
    520 		}
    521 	;
    522 LOCAL_ATTR
    523 	:	'$' ID {enclosingRule!=null && enclosingRule.getLocalAttributeScope($ID.text)!=null}?
    524 		//{System.out.println("found \$localattr");}
    525 		{
    526 		ST st;
    527 		AttributeScope scope = enclosingRule.getLocalAttributeScope($ID.text);
    528 		if ( scope.isPredefinedRuleScope ) {
    529 			st = template("rulePropertyRef_"+$ID.text);
    530 			grammar.referenceRuleLabelPredefinedAttribute(enclosingRule.name);
    531 			st.add("scope", enclosingRule.name);
    532 			st.add("attr", $ID.text);
    533 		}
    534 		else if ( scope.isPredefinedLexerRuleScope ) {
    535 			st = template("lexerRulePropertyRef_"+$ID.text);
    536 			st.add("scope", enclosingRule.name);
    537 			st.add("attr", $ID.text);
    538 		}
    539 		else if ( scope.isParameterScope ) {
    540 			st = template("parameterAttributeRef");
    541 			st.add("attr", scope.getAttribute($ID.text));
    542 		}
    543 		else {
    544 			st = template("returnAttributeRef");
    545 			st.add("ruleDescriptor", enclosingRule);
    546 			st.add("attr", scope.getAttribute($ID.text));
    547 		}
    548 		}
    549 	;
    550 
    551 /**	$x::y	the only way to access the attributes within a dynamic scope
    552  * 			regardless of whether or not you are in the defining rule.
    553  *
    554  * 			scope Symbols { List names; }
    555  * 			r
    556  * 			scope {int i;}
    557  * 			scope Symbols;
    558  * 				:	{$r::i=3;} s {$Symbols::names;}
    559  * 				;
    560  * 			s	:	{$r::i; $Symbols::names;}
    561  * 				;
    562  */
    563 SET_DYNAMIC_SCOPE_ATTR
    564 	:	'$' x=ID '::' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';'
    565 						   {resolveDynamicScope($x.text)!=null &&
    566 						     resolveDynamicScope($x.text).getAttribute($y.text)!=null}?
    567 		//{System.out.println("found set \$scope::attr "+ $x.text + "::" + $y.text + " to " + $expr.text);}
    568 		{
    569 		AttributeScope scope = resolveDynamicScope($x.text);
    570 		if ( scope!=null ) {
    571 			ST st = template("scopeSetAttributeRef");
    572 			st.add("scope", $x.text);
    573 			st.add("attr",  scope.getAttribute($y.text));
    574 			st.add("expr",  translateAction($expr.text));
    575 		}
    576 		else {
    577 			// error: invalid dynamic attribute
    578 		}
    579 		}
    580 	;
    581 
    582 DYNAMIC_SCOPE_ATTR
    583 	:	'$' x=ID '::' y=ID
    584 						   {resolveDynamicScope($x.text)!=null &&
    585 						     resolveDynamicScope($x.text).getAttribute($y.text)!=null}?
    586 		//{System.out.println("found \$scope::attr "+ $x.text + "::" + $y.text);}
    587 		{
    588 		AttributeScope scope = resolveDynamicScope($x.text);
    589 		if ( scope!=null ) {
    590 			ST st = template("scopeAttributeRef");
    591 			st.add("scope", $x.text);
    592 			st.add("attr",  scope.getAttribute($y.text));
    593 		}
    594 		else {
    595 			// error: invalid dynamic attribute
    596 		}
    597 		}
    598 	;
    599 
    600 
    601 ERROR_SCOPED_XY
    602 	:	'$' x=ID '::' y=ID
    603 		{
    604 		chunks.add(getText());
    605 		generator.issueInvalidScopeError($x.text,$y.text,
    606 		                                 enclosingRule,actionToken,
    607 		                                 outerAltNum);
    608 		}
    609 	;
    610 
    611 /**		To access deeper (than top of stack) scopes, use the notation:
    612  *
    613  * 		$x[-1]::y previous (just under top of stack)
    614  * 		$x[-i]::y top of stack - i where the '-' MUST BE PRESENT;
    615  * 				  i.e., i cannot simply be negative without the '-' sign!
    616  * 		$x[i]::y  absolute index i (0..size-1)
    617  * 		$x[0]::y  is the absolute 0 indexed element (bottom of the stack)
    618  */
    619 DYNAMIC_NEGATIVE_INDEXED_SCOPE_ATTR
    620 	:	'$' x=ID '[' '-' expr=SCOPE_INDEX_EXPR ']' '::' y=ID
    621 		// {System.out.println("found \$scope[-...]::attr");}
    622 		{
    623 		ST st = template("scopeAttributeRef");
    624 		st.add("scope",    $x.text);
    625 		st.add("attr",     resolveDynamicScope($x.text).getAttribute($y.text));
    626 		st.add("negIndex", $expr.text);
    627 		}
    628 	;
    629 
    630 DYNAMIC_ABSOLUTE_INDEXED_SCOPE_ATTR
    631 	:	'$' x=ID '[' expr=SCOPE_INDEX_EXPR ']' '::' y=ID
    632 		// {System.out.println("found \$scope[...]::attr");}
    633 		{
    634 		ST st = template("scopeAttributeRef");
    635 		st.add("scope", $x.text);
    636 		st.add("attr",  resolveDynamicScope($x.text).getAttribute($y.text));
    637 		st.add("index", $expr.text);
    638 		}
    639 	;
    640 
    641 fragment
    642 SCOPE_INDEX_EXPR
    643 	:	(~']')+
    644 	;
    645 
    646 /** $r		y is a rule's dynamic scope or a global shared scope.
    647  * 			Isolated $rulename is not allowed unless it has a dynamic scope *and*
    648  * 			there is no reference to rulename in the enclosing alternative,
    649  * 			which would be ambiguous.  See TestAttributes.testAmbiguousRuleRef()
    650  */
    651 ISOLATED_DYNAMIC_SCOPE
    652 	:	'$' ID {resolveDynamicScope($ID.text)!=null}?
    653 		// {System.out.println("found isolated \$scope where scope is a dynamic scope");}
    654 		{
    655 		ST st = template("isolatedDynamicScopeRef");
    656 		st.add("scope", $ID.text);
    657 		}
    658 	;
    659 
    660 // antlr.g then codegen.g does these first two currently.
    661 // don't want to duplicate that code.
    662 
    663 /** %foo(a={},b={},...) ctor */
    664 TEMPLATE_INSTANCE
    665 	:	'%' ID '(' ( WS? ARG (',' WS? ARG)* WS? )? ')'
    666 		// {System.out.println("found \%foo(args)");}
    667 		{
    668 		String action = getText().substring(1,getText().length());
    669 		String ruleName = "<outside-of-rule>";
    670 		if ( enclosingRule!=null ) {
    671 			ruleName = enclosingRule.name;
    672 		}
    673 		ST st =
    674 			generator.translateTemplateConstructor(ruleName,
    675 												   outerAltNum,
    676 												   actionToken,
    677 												   action);
    678 		if ( st!=null ) {
    679 			chunks.add(st);
    680 		}
    681 		}
    682 	;
    683 
    684 /** %({name-expr})(a={},...) indirect template ctor reference */
    685 INDIRECT_TEMPLATE_INSTANCE
    686 	:	'%' '(' ACTION ')' '(' ( WS? ARG (',' WS? ARG)* WS? )? ')'
    687 		// {System.out.println("found \%({...})(args)");}
    688 		{
    689 		String action = getText().substring(1,getText().length());
    690 		ST st =
    691 			generator.translateTemplateConstructor(enclosingRule.name,
    692 												   outerAltNum,
    693 												   actionToken,
    694 												   action);
    695 		chunks.add(st);
    696 		}
    697 	;
    698 
    699 fragment
    700 ARG	:	ID '=' ACTION
    701 	;
    702 
    703 /**	%{expr}.y = z; template attribute y of ST-typed expr to z */
    704 SET_EXPR_ATTRIBUTE
    705 	:	'%' a=ACTION '.' ID WS? '=' expr=ATTR_VALUE_EXPR ';'
    706 		// {System.out.println("found \%{expr}.y = z;");}
    707 		{
    708 		ST st = template("actionSetAttribute");
    709 		String action = $a.text;
    710 		action = action.substring(1,action.length()-1); // stuff inside {...}
    711 		st.add("st", translateAction(action));
    712 		st.add("attrName", $ID.text);
    713 		st.add("expr", translateAction($expr.text));
    714 		}
    715 	;
    716 
    717 /*    %x.y = z; set template attribute y of x (always set never get attr)
    718  *              to z [languages like python without ';' must still use the
    719  *              ';' which the code generator is free to remove during code gen]
    720  */
    721 SET_ATTRIBUTE
    722 	:	'%' x=ID '.' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';'
    723 		// {System.out.println("found \%x.y = z;");}
    724 		{
    725 		ST st = template("actionSetAttribute");
    726 		st.add("st", $x.text);
    727 		st.add("attrName", $y.text);
    728 		st.add("expr", translateAction($expr.text));
    729 		}
    730 	;
    731 
    732 /** Don't allow an = as first char to prevent $x == 3; kind of stuff. */
    733 fragment
    734 ATTR_VALUE_EXPR
    735 	:	~'=' (~';')*
    736 	;
    737 
    738 /** %{string-expr} anonymous template from string expr */
    739 TEMPLATE_EXPR
    740 	:	'%' a=ACTION
    741 		// {System.out.println("found \%{expr}");}
    742 		{
    743 		ST st = template("actionStringConstructor");
    744 		String action = $a.text;
    745 		action = action.substring(1,action.length()-1); // stuff inside {...}
    746 		st.add("stringExpr", translateAction(action));
    747 		}
    748 	;
    749 
    750 fragment
    751 ACTION
    752 	:	'{' (options {greedy=false;}:.)* '}'
    753 	;
    754 
    755 ESC :   '\\' '$' {chunks.add("\$");}
    756 	|	'\\' '%' {chunks.add("\%");}
    757 	|	'\\' ~('$'|'%') {chunks.add(getText());}
    758     ;
    759 
    760 ERROR_XY
    761 	:	'$' x=ID '.' y=ID
    762 		{
    763 		chunks.add(getText());
    764 		generator.issueInvalidAttributeError($x.text,$y.text,
    765 		                                     enclosingRule,actionToken,
    766 		                                     outerAltNum);
    767 		}
    768 	;
    769 
    770 ERROR_X
    771 	:	'$' x=ID
    772 		{
    773 		chunks.add(getText());
    774 		generator.issueInvalidAttributeError($x.text,
    775 		                                     enclosingRule,actionToken,
    776 		                                     outerAltNum);
    777 		}
    778 	;
    779 
    780 UNKNOWN_SYNTAX
    781 	:	'$'
    782 		{
    783 		chunks.add(getText());
    784 		// shouldn't need an error here.  Just accept \$ if it doesn't look like anything
    785 		}
    786 	|	'%' (ID|'.'|'('|')'|','|'{'|'}'|'"')*
    787 		{
    788 		chunks.add(getText());
    789 		ErrorManager.grammarError(ErrorManager.MSG_INVALID_TEMPLATE_ACTION,
    790 								  grammar,
    791 								  actionToken,
    792 								  getText());
    793 		}
    794 	;
    795 
    796 TEXT:	~('$'|'%'|'\\')+ {chunks.add(getText());}
    797 	;
    798 
    799 fragment
    800 ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
    801     ;
    802 
    803 fragment
    804 INT :	'0'..'9'+
    805 	;
    806 
    807 fragment
    808 WS	:	(' '|'\t'|'\n'|'\r')+
    809 	;
    810