• Home
  • History
  • Annotate
  • 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  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 {
35package org.antlr.grammar.v3;
36import org.stringtemplate.v4.ST;
37import org.antlr.runtime.*;
38import org.antlr.tool.*;
39import org.antlr.codegen.*;
40
41import org.antlr.runtime.*;
42import java.util.List;
43import java.util.ArrayList;
44import org.antlr.grammar.v3.ANTLRParser;
45
46}
47
48@members {
49public List chunks = new ArrayList();
50Rule enclosingRule;
51int outerAltNum;
52Grammar grammar;
53CodeGenerator generator;
54Token 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 */
84public 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
93public 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
106public 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
118public boolean isTokenRefInAlt(String id) {
119    return enclosingRule.getTokenRefsInAlt(id, outerAltNum)!=null;
120}
121public boolean isRuleRefInAlt(String id) {
122    return enclosingRule.getRuleRefsInAlt(id, outerAltNum)!=null;
123}
124public Grammar.LabelElementPair getElementLabel(String id) {
125    return enclosingRule.getLabel(id);
126}
127
128public 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 */
147public 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
156AttributeScope 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
167protected 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 */
183SET_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	;
228ENCLOSING_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. */
268SET_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 */
287TOKEN_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 */
321SET_RULE_SCOPE_ATTR
322@init {
323Grammar.LabelElementPair pair=null;
324String 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*/
350RULE_SCOPE_ATTR
351@init {
352Grammar.LabelElementPair pair=null;
353String 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 */
412LABEL_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 */
434ISOLATED_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 */
454ISOLATED_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 */
486SET_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	;
522LOCAL_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 */
563SET_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
582DYNAMIC_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
601ERROR_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 */
619DYNAMIC_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
630DYNAMIC_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
641fragment
642SCOPE_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 */
651ISOLATED_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 */
664TEMPLATE_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 */
685INDIRECT_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
699fragment
700ARG	:	ID '=' ACTION
701	;
702
703/**	%{expr}.y = z; template attribute y of ST-typed expr to z */
704SET_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 */
721SET_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. */
733fragment
734ATTR_VALUE_EXPR
735	:	~'=' (~';')*
736	;
737
738/** %{string-expr} anonymous template from string expr */
739TEMPLATE_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
750fragment
751ACTION
752	:	'{' (options {greedy=false;}:.)* '}'
753	;
754
755ESC :   '\\' '$' {chunks.add("\$");}
756	|	'\\' '%' {chunks.add("\%");}
757	|	'\\' ~('$'|'%') {chunks.add(getText());}
758    ;
759
760ERROR_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
770ERROR_X
771	:	'$' x=ID
772		{
773		chunks.add(getText());
774		generator.issueInvalidAttributeError($x.text,
775		                                     enclosingRule,actionToken,
776		                                     outerAltNum);
777		}
778	;
779
780UNKNOWN_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
796TEXT:	~('$'|'%'|'\\')+ {chunks.add(getText());}
797	;
798
799fragment
800ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
801    ;
802
803fragment
804INT :	'0'..'9'+
805	;
806
807fragment
808WS	:	(' '|'\t'|'\n'|'\r')+
809	;
810