/*
 [The "BSD licence"]
 Copyright (c) 2005-2006 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.
*/
package org.antlr.test;

import org.antlr.Tool;
import org.antlr.codegen.ActionTranslator;
import org.antlr.codegen.CodeGenerator;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.antlr.stringtemplate.language.AngleBracketTemplateLexer;
import org.antlr.tool.*;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

/** Check the $x, $x.y attributes.  For checking the actual
 *  translation, assume the Java target.  This is still a great test
 *  for the semantics of the $x.y stuff regardless of the target.
 */
public class TestAttributes extends BaseTest {

	/** Public default constructor used by TestRig */
	public TestAttributes() {
	}

	public void testEscapedLessThanInAction() throws Exception {
		Grammar g = new Grammar();
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		String action = "i<3; '<xmltag>'";
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),0);
		String expecting = action;
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, "<action>");
		actionST.setAttribute("action", rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);
	}

	public void testEscaped$InAction() throws Exception {
		String action = "int \\$n; \"\\$in string\\$\"";
		String expecting = "int $n; \"$in string$\"";
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"@members {"+action+"}\n"+
			"a[User u, int i]\n" +
			"        : {"+action+"}\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator =
			new ActionTranslator(generator,
									  "a",
									  new antlr.CommonToken(ANTLRParser.ACTION,action),0);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);
	}

	public void testArguments() throws Exception {
		String action = "$i; $i.x; $u; $u.x";
		String expecting = "i; i.x; u; u.x";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[User u, int i]\n" +
			"        : {"+action+"}\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testComplicatedArgParsing() throws Exception {
		String action = "x, (*a).foo(21,33), 3.2+1, '\\n', "+
						"\"a,oo\\nick\", {bl, \"fdkj\"eck}";
		String expecting = "x, (*a).foo(21,33), 3.2+1, '\\n', \"a,oo\\nick\", {bl, \"fdkj\"eck}";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);

		// now check in actual grammar.
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[User u, int i]\n" +
			"        : A a["+action+"] B\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =	translator.translate();
		assertEquals(expecting, rawTranslation);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testBracketArgParsing() throws Exception {
		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);

		// now check in actual grammar.
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[String[\\] ick, int i]\n" +
			"        : A \n"+
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		Rule r = g.getRule("a");
		AttributeScope parameters = r.parameterScope;
		List<Attribute> attrs = parameters.getAttributes();
		assertEquals("attribute mismatch","String[] ick",attrs.get(0).decl.toString());
		assertEquals("parameter name mismatch","ick",attrs.get(0).name);
		assertEquals("declarator mismatch", "String[]", attrs.get(0).type);

		assertEquals("attribute mismatch","int i",attrs.get(1).decl.toString());
		assertEquals("parameter name mismatch","i",attrs.get(1).name);
		assertEquals("declarator mismatch", "int", attrs.get(1).type);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testStringArgParsing() throws Exception {
		String action = "34, '{', \"it's<\", '\"', \"\\\"\", 19";
		String expecting = "34, '{', \"it's<\", '\"', \"\\\"\", 19";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);

		// now check in actual grammar.
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[User u, int i]\n" +
			"        : A a["+action+"] B\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =	translator.translate();
		assertEquals(expecting, rawTranslation);

		List<String> expectArgs = new ArrayList<String>() {
			{add("34");}
			{add("'{'");}
			{add("\"it's<\"");}
			{add("'\"'");}
			{add("\"\\\"\"");} // that's "\""
			{add("19");}
		};
		List<String> actualArgs = CodeGenerator.getListOfArgumentsFromAction(action, ',');
		assertEquals("args mismatch", expectArgs, actualArgs);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testComplicatedSingleArgParsing() throws Exception {
		String action = "(*a).foo(21,33,\",\")";
		String expecting = "(*a).foo(21,33,\",\")";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);

		// now check in actual grammar.
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[User u, int i]\n" +
			"        : A a["+action+"] B\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =	translator.translate();
		assertEquals(expecting, rawTranslation);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testArgWithLT() throws Exception {
		String action = "34<50";
		String expecting = "34<50";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);

		// now check in actual grammar.
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[boolean b]\n" +
			"        : A a["+action+"] B\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		assertEquals(expecting, rawTranslation);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testGenericsAsArgumentDefinition() throws Exception {
		String action = "$foo.get(\"ick\");";
		String expecting = "foo.get(\"ick\");";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		String grammar =
			"parser grammar T;\n"+
			"a[HashMap<String,String> foo]\n" +
			"        : {"+action+"}\n" +
			"        ;";
		Grammar g = new Grammar(grammar);
		Rule ra = g.getRule("a");
		List<Attribute> attrs = ra.parameterScope.getAttributes();
		assertEquals("attribute mismatch","HashMap<String,String> foo",attrs.get(0).decl.toString());
		assertEquals("parameter name mismatch","foo",attrs.get(0).name);
		assertEquals("declarator mismatch", "HashMap<String,String>", attrs.get(0).type);

		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testGenericsAsArgumentDefinition2() throws Exception {
		String action = "$foo.get(\"ick\"); x=3;";
		String expecting = "foo.get(\"ick\"); x=3;";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		String grammar =
			"parser grammar T;\n"+
			"a[HashMap<String,String> foo, int x, List<String> duh]\n" +
			"        : {"+action+"}\n" +
			"        ;";
		Grammar g = new Grammar(grammar);
		Rule ra = g.getRule("a");
		List<Attribute> attrs = ra.parameterScope.getAttributes();

		assertEquals("attribute mismatch","HashMap<String,String> foo",attrs.get(0).decl.toString().trim());
		assertEquals("parameter name mismatch","foo",attrs.get(0).name);
		assertEquals("declarator mismatch", "HashMap<String,String>", attrs.get(0).type);

		assertEquals("attribute mismatch","int x",attrs.get(1).decl.toString().trim());
		assertEquals("parameter name mismatch","x",attrs.get(1).name);
		assertEquals("declarator mismatch", "int", attrs.get(1).type);

		assertEquals("attribute mismatch","List<String> duh",attrs.get(2).decl.toString().trim());
		assertEquals("parameter name mismatch","duh",attrs.get(2).name);
		assertEquals("declarator mismatch", "List<String>", attrs.get(2).type);

		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testGenericsAsReturnValue() throws Exception {
		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		String grammar =
			"parser grammar T;\n"+
			"a returns [HashMap<String,String> foo] : ;\n";
		Grammar g = new Grammar(grammar);
		Rule ra = g.getRule("a");
		List<Attribute> attrs = ra.returnScope.getAttributes();
		assertEquals("attribute mismatch","HashMap<String,String> foo",attrs.get(0).decl.toString());
		assertEquals("parameter name mismatch","foo",attrs.get(0).name);
		assertEquals("declarator mismatch", "HashMap<String,String>", attrs.get(0).type);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testComplicatedArgParsingWithTranslation() throws Exception {
		String action = "x, $A.text+\"3242\", (*$A).foo(21,33), 3.2+1, '\\n', "+
						"\"a,oo\\nick\", {bl, \"fdkj\"eck}";
		String expecting = "x, (A1!=null?A1.getText():null)+\"3242\", (*A1).foo(21,33), 3.2+1, '\\n', \"a,oo\\nick\", {bl, \"fdkj\"eck}";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);

		// now check in actual grammar.
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[User u, int i]\n" +
			"        : A a["+action+"] B\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	/** $x.start refs are checked during translation not before so ANTLR misses
	 the fact that rule r has refs to predefined attributes if the ref is after
	 the def of the method or self-referential.  Actually would be ok if I didn't
	 convert actions to strings; keep as templates.
	 June 9, 2006: made action translation leave templates not strings
	 */
	public void testRefToReturnValueBeforeRefToPredefinedAttr() throws Exception {
		String action = "$x.foo";
		String expecting = "(x!=null?x.foo:0)";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a : x=b {"+action+"} ;\n" +
			"b returns [int foo] : B {$b.start} ;\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testRuleLabelBeforeRefToPredefinedAttr() throws Exception {
		// As of Mar 2007, I'm removing unused labels.  Unfortunately,
		// the action is not seen until code gen.  Can't see $x.text
		// before stripping unused labels.  We really need to translate
		// actions first so code gen logic can use info.
		String action = "$x.text";
		String expecting = "(x!=null?input.toString(x.start,x.stop):null)";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a : x=b {"+action+"} ;\n" +
			"b : B ;\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testInvalidArguments() throws Exception {
		String action = "$x";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[User u, int i]\n" +
			"        : {"+action+"}\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator,
																	 "a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		int expectedMsgID = ErrorManager.MSG_UNKNOWN_SIMPLE_ATTRIBUTE;
		Object expectedArg = "x";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
		checkError(equeue, expectedMessage);
	}

	public void testReturnValue() throws Exception {
		String action = "$x.i";
		String expecting = "x";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"a returns [int i]\n" +
			"        : 'a'\n" +
			"        ;\n" +
			"b : x=a {"+action+"} ;\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator =
			new ActionTranslator(generator,
									  "b",
									  new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testReturnValueWithNumber() throws Exception {
		String action = "$x.i1";
		String expecting = "x";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"a returns [int i1]\n" +
			"        : 'a'\n" +
			"        ;\n" +
			"b : x=a {"+action+"} ;\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator =
			new ActionTranslator(generator,
									  "b",
									  new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testReturnValues() throws Exception {
		String action = "$i; $i.x; $u; $u.x";
		String expecting = "retval.i; retval.i.x; retval.u; retval.u.x";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a returns [User u, int i]\n" +
			"        : {"+action+"}\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	/* regression test for ANTLR-46 */
	public void testReturnWithMultipleRuleRefs() throws Exception {
		String action1 = "$obj = $rule2.obj;";
		String action2 = "$obj = $rule3.obj;";
		String expecting1 = "obj = rule21;";
		String expecting2 = "obj = rule32;";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n" +
			"rule1 returns [ Object obj ]\n" +
			":	rule2 { "+action1+" }\n" +
			"|	rule3 { "+action2+" }\n" +
			";\n"+
			"rule2 returns [ Object obj ]\n"+
			":	foo='foo' { $obj = $foo.text; }\n"+
			";\n"+
			"rule3 returns [ Object obj ]\n"+
			":	bar='bar' { $obj = $bar.text; }\n"+
			";");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		int i = 0;
		String action = action1;
		String expecting = expecting1;
		do {
			ActionTranslator translator = new ActionTranslator(generator,"rule1",
																		 new antlr.CommonToken(ANTLRParser.ACTION,action),i+1);
			String rawTranslation =
					translator.translate();
			StringTemplateGroup templates =
					new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
			StringTemplate actionST = new StringTemplate(templates, rawTranslation);
			String found = actionST.toString();
			assertEquals(expecting, found);
			action = action2;
			expecting = expecting2;
		} while (i++ < 1);
		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testInvalidReturnValues() throws Exception {
		String action = "$x";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a returns [User u, int i]\n" +
			"        : {"+action+"}\n" +
			"        ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		int expectedMsgID = ErrorManager.MSG_UNKNOWN_SIMPLE_ATTRIBUTE;
		Object expectedArg = "x";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
		checkError(equeue, expectedMessage);
	}

	public void testTokenLabels() throws Exception {
		String action = "$id; $f; $id.text; $id.getText(); $id.dork " +
						"$id.type; $id.line; $id.pos; " +
						"$id.channel; $id.index;";
		String expecting = "id; f; (id!=null?id.getText():null); id.getText(); id.dork (id!=null?id.getType():0); (id!=null?id.getLine():0); (id!=null?id.getCharPositionInLine():0); (id!=null?id.getChannel():0); (id!=null?id.getTokenIndex():0);";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a : id=ID f=FLOAT {"+action+"}\n" +
			"  ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testRuleLabels() throws Exception {
		String action = "$r.x; $r.start;\r\n $r.stop;\r\n $r.tree; $a.x; $a.stop;";
		String expecting = "(r!=null?r.x:0); (r!=null?((Token)r.start):null);\r\n" +
						   "             (r!=null?((Token)r.stop):null);\r\n" +
						   "             (r!=null?((Object)r.tree):null); (r!=null?r.x:0); (r!=null?((Token)r.stop):null);";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a returns [int x]\n" +
			"  :\n" +
			"  ;\n"+
			"b : r=a {###"+action+"!!!}\n" +
			"  ;");
		Tool antlr = newTool();
		antlr.setOutputDirectory(null); // write to /dev/null
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // codegen phase sets some vars we need
		StringTemplate codeST = generator.getRecognizerST();
		String code = codeST.toString();
		String found = code.substring(code.indexOf("###")+3,code.indexOf("!!!"));
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testAmbiguRuleRef() throws Exception {
		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a : A a {$a.text} | B ;");
		Tool antlr = newTool();
		antlr.setOutputDirectory(null); // write to /dev/null
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer();

		// error(132): <string>:2:9: reference $a is ambiguous; rule a is enclosing rule and referenced in the production
		assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size());
	}

	public void testRuleLabelsWithSpecialToken() throws Exception {
		String action = "$r.x; $r.start; $r.stop; $r.tree; $a.x; $a.stop;";
		String expecting = "(r!=null?r.x:0); (r!=null?((MYTOKEN)r.start):null); (r!=null?((MYTOKEN)r.stop):null); (r!=null?((Object)r.tree):null); (r!=null?r.x:0); (r!=null?((MYTOKEN)r.stop):null);";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"options {TokenLabelType=MYTOKEN;}\n"+
			"a returns [int x]\n" +
			"  :\n" +
			"  ;\n"+
			"b : r=a {###"+action+"!!!}\n" +
			"  ;");
		Tool antlr = newTool();
		antlr.setOutputDirectory(null); // write to /dev/null
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // codegen phase sets some vars we need

		StringTemplate codeST = generator.getRecognizerST();
		String code = codeST.toString();
		String found = code.substring(code.indexOf("###")+3,code.indexOf("!!!"));
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testForwardRefRuleLabels() throws Exception {
		String action = "$r.x; $r.start; $r.stop; $r.tree; $a.x; $a.tree;";
		String expecting = "(r!=null?r.x:0); (r!=null?((Token)r.start):null); (r!=null?((Token)r.stop):null); (r!=null?((Object)r.tree):null); (r!=null?r.x:0); (r!=null?((Object)r.tree):null);";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"b : r=a {###"+action+"!!!}\n" +
			"  ;\n" +
			"a returns [int x]\n" +
			"  : ;\n");
		Tool antlr = newTool();
		antlr.setOutputDirectory(null); // write to /dev/null
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // codegen phase sets some vars we need

		StringTemplate codeST = generator.getRecognizerST();
		String code = codeST.toString();
		String found = code.substring(code.indexOf("###")+3,code.indexOf("!!!"));
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testInvalidRuleLabelAccessesParameter() throws Exception {
		String action = "$r.z";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[int z] returns [int x]\n" +
			"  :\n" +
			"  ;\n"+
			"b : r=a[3] {"+action+"}\n" +
			"  ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator, "b",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		int expectedMsgID = ErrorManager.MSG_INVALID_RULE_PARAMETER_REF;
		Object expectedArg = "a";
		Object expectedArg2 = "z";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
		checkError(equeue, expectedMessage);
	}

	public void testInvalidRuleLabelAccessesScopeAttribute() throws Exception {
		String action = "$r.n";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a\n" +
			"scope { int n; }\n" +
			"  :\n" +
			"  ;\n"+
			"b : r=a[3] {"+action+"}\n" +
			"  ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator, "b",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		int expectedMsgID = ErrorManager.MSG_INVALID_RULE_SCOPE_ATTRIBUTE_REF;
		Object expectedArg = "a";
		Object expectedArg2 = "n";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
		checkError(equeue, expectedMessage);
	}

	public void testInvalidRuleAttribute() throws Exception {
		String action = "$r.blort";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[int z] returns [int x]\n" +
			"  :\n" +
			"  ;\n"+
			"b : r=a[3] {"+action+"}\n" +
			"  ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator, "b",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		int expectedMsgID = ErrorManager.MSG_UNKNOWN_RULE_ATTRIBUTE;
		Object expectedArg = "a";
		Object expectedArg2 = "blort";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
		checkError(equeue, expectedMessage);
	}

	public void testMissingRuleAttribute() throws Exception {
		String action = "$r";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a[int z] returns [int x]\n" +
			"  :\n" +
			"  ;\n"+
			"b : r=a[3] {"+action+"}\n" +
			"  ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator, "b",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();

		int expectedMsgID = ErrorManager.MSG_ISOLATED_RULE_SCOPE;
		Object expectedArg = "r";
		Object expectedArg2 = null;
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
		checkError(equeue, expectedMessage);
	}

	public void testMissingUnlabeledRuleAttribute() throws Exception {
		String action = "$a";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"a returns [int x]:\n" +
			"  ;\n"+
			"b : a {"+action+"}\n" +
			"  ;");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator, "b",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();

		int expectedMsgID = ErrorManager.MSG_ISOLATED_RULE_SCOPE;
		Object expectedArg = "a";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
		checkError(equeue, expectedMessage);
	}

	public void testNonDynamicAttributeOutsideRule() throws Exception {
		String action = "public void foo() { $x; }";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"@members {'+action+'}\n" +
			"a : ;\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator,
																	 null,
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),0);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		int expectedMsgID = ErrorManager.MSG_ATTRIBUTE_REF_NOT_IN_RULE;
		Object expectedArg = "x";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
		checkError(equeue, expectedMessage);
	}

	public void testNonDynamicAttributeOutsideRule2() throws Exception {
		String action = "public void foo() { $x.y; }";
		String expecting = action;

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"parser grammar t;\n"+
			"@members {'+action+'}\n" +
			"a : ;\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		ActionTranslator translator = new ActionTranslator(generator,
																	 null,
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),0);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		int expectedMsgID = ErrorManager.MSG_ATTRIBUTE_REF_NOT_IN_RULE;
		Object expectedArg = "x";
		Object expectedArg2 = "y";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
		checkError(equeue, expectedMessage);
	}

	// D Y N A M I C A L L Y  S C O P E D  A T T R I B U T E S

	public void testBasicGlobalScope() throws Exception {
		String action = "$Symbols::names.add($id.text);";
		String expecting = "((Symbols_scope)Symbols_stack.peek()).names.add((id!=null?id.getText():null));";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"scope Symbols {\n" +
			"  int n;\n" +
			"  List names;\n" +
			"}\n" +
			"a scope Symbols; : (id=ID ';' {"+action+"} )+\n" +
			"  ;\n" +
			"ID : 'a';\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testUnknownGlobalScope() throws Exception {
		String action = "$Symbols::names.add($id.text);";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"a scope Symbols; : (id=ID ';' {"+action+"} )+\n" +
			"  ;\n" +
			"ID : 'a';\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);

		assertEquals("unexpected errors: "+equeue, 2, equeue.errors.size());

		int expectedMsgID = ErrorManager.MSG_UNKNOWN_DYNAMIC_SCOPE;
		Object expectedArg = "Symbols";
		GrammarSemanticsMessage expectedMessage =
			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
		checkError(equeue, expectedMessage);
	}

	public void testIndexedGlobalScope() throws Exception {
		String action = "$Symbols[-1]::names.add($id.text);";
		String expecting =
			"((Symbols_scope)Symbols_stack.elementAt(Symbols_stack.size()-1-1)).names.add((id!=null?id.getText():null));";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"scope Symbols {\n" +
			"  int n;\n" +
			"  List names;\n" +
			"}\n" +
			"a scope Symbols; : (id=ID ';' {"+action+"} )+\n" +
			"  ;\n" +
			"ID : 'a';\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void test0IndexedGlobalScope() throws Exception {
		String action = "$Symbols[0]::names.add($id.text);";
		String expecting =
			"((Symbols_scope)Symbols_stack.elementAt(0)).names.add((id!=null?id.getText():null));";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"scope Symbols {\n" +
			"  int n;\n" +
			"  List names;\n" +
			"}\n" +
			"a scope Symbols; : (id=ID ';' {"+action+"} )+\n" +
			"  ;\n" +
			"ID : 'a';\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		assertEquals(expecting, rawTranslation);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testAbsoluteIndexedGlobalScope() throws Exception {
		String action = "$Symbols[3]::names.add($id.text);";
		String expecting =
			"((Symbols_scope)Symbols_stack.elementAt(3)).names.add((id!=null?id.getText():null));";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"scope Symbols {\n" +
			"  int n;\n" +
			"  List names;\n" +
			"}\n" +
			"a scope Symbols; : (id=ID ';' {"+action+"} )+\n" +
			"  ;\n" +
			"ID : 'a';\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		assertEquals(expecting, rawTranslation);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testScopeAndAttributeWithUnderscore() throws Exception {
		String action = "$foo_bar::a_b;";
		String expecting = "((foo_bar_scope)foo_bar_stack.peek()).a_b;";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"scope foo_bar {\n" +
			"  int a_b;\n" +
			"}\n" +
			"a scope foo_bar; : (ID {"+action+"} )+\n" +
			"  ;\n" +
			"ID : 'a';\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();

		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testSharedGlobalScope() throws Exception {
		String action = "$Symbols::x;";
		String expecting = "((Symbols_scope)Symbols_stack.peek()).x;";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"scope Symbols {\n" +
			"  String x;\n" +
			"}\n" +
			"a\n"+
			"scope { int y; }\n"+
			"scope Symbols;\n" +
			" : b {"+action+"}\n" +
			" ;\n" +
			"b : ID {$Symbols::x=$ID.text} ;\n" +
			"ID : 'a';\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testGlobalScopeOutsideRule() throws Exception {
		String action = "public void foo() {$Symbols::names.add('foo');}";
		String expecting = "public void foo() {((Symbols_scope)Symbols_stack.peek()).names.add('foo');}";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"scope Symbols {\n" +
			"  int n;\n" +
			"  List names;\n" +
			"}\n" +
			"@members {'+action+'}\n" +
			"a : \n" +
			"  ;\n");
		Tool antlr = newTool();
		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
		g.setCodeGenerator(generator);
		generator.genRecognizer(); // forces load of templates
		ActionTranslator translator = new ActionTranslator(generator,"a",
																	 new antlr.CommonToken(ANTLRParser.ACTION,action),1);
		String rawTranslation =
			translator.translate();
		StringTemplateGroup templates =
			new StringTemplateGroup(".", AngleBracketTemplateLexer.class);
		StringTemplate actionST = new StringTemplate(templates, rawTranslation);
		String found = actionST.toString();
		assertEquals(expecting, found);

		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
	}

	public void testRuleScopeOutsideRule() throws Exception {
		String action = "public void foo() {$a::name;}";
		String expecting = "public void foo() {((a_scope)a_stack.peek()).name;}";

		ErrorQueue equeue = new ErrorQueue();
		ErrorManager.setErrorListener(equeue);
		Grammar g = new Grammar(
			"grammar t;\n"+
			"@m