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