Home | History | Annotate | Download | only in test
      1 /*
      2  * [The "BSD license"]
      3  *  Copyright (c) 2010 Terence Parr
      4  *  All rights reserved.
      5  *
      6  *  Redistribution and use in source and binary forms, with or without
      7  *  modification, are permitted provided that the following conditions
      8  *  are met:
      9  *  1. Redistributions of source code must retain the above copyright
     10  *      notice, this list of conditions and the following disclaimer.
     11  *  2. Redistributions in binary form must reproduce the above copyright
     12  *      notice, this list of conditions and the following disclaimer in the
     13  *      documentation and/or other materials provided with the distribution.
     14  *  3. The name of the author may not be used to endorse or promote products
     15  *      derived from this software without specific prior written permission.
     16  *
     17  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 package org.antlr.test;
     29 
     30 import org.antlr.Tool;
     31 import org.antlr.tool.*;
     32 import org.junit.Test;
     33 
     34 import java.io.File;
     35 
     36 public class TestCompositeGrammars extends BaseTest {
     37 	protected boolean debug = false;
     38 
     39 	@Test public void testWildcardStillWorks() throws Exception {
     40 		ErrorQueue equeue = new ErrorQueue();
     41 		ErrorManager.setErrorListener(equeue);
     42 		String grammar =
     43 			"parser grammar S;\n" +
     44 			"a : B . C ;\n"; // not qualified ID
     45 		Grammar g = new Grammar(grammar);
     46 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
     47 	}
     48 
     49 	@Test public void testDelegatorInvokesDelegateRule() throws Exception {
     50 		String slave =
     51 			"parser grammar S;\n" +
     52 			"a : B {System.out.println(\"S.a\");} ;\n";
     53 		mkdir(tmpdir);
     54 		writeFile(tmpdir, "S.g", slave);
     55 		String master =
     56 			"grammar M;\n" +
     57 			"import S;\n" +
     58 			"s : a ;\n" +
     59 			"B : 'b' ;" + // defines B from inherited token space
     60 			"WS : (' '|'\\n') {skip();} ;\n" ;
     61 		String found = execParser("M.g", master, "MParser", "MLexer",
     62 								  "s", "b", debug);
     63 		assertEquals("S.a\n", found);
     64 	}
     65 
     66 	@Test public void testDelegatorInvokesDelegateRuleWithArgs() throws Exception {
     67 		// must generate something like:
     68 		// public int a(int x) throws RecognitionException { return gS.a(x); }
     69 		// in M.
     70 		String slave =
     71 			"parser grammar S;\n" +
     72 			"a[int x] returns [int y] : B {System.out.print(\"S.a\"); $y=1000;} ;\n";
     73 		mkdir(tmpdir);
     74 		writeFile(tmpdir, "S.g", slave);
     75 		String master =
     76 			"grammar M;\n" +
     77 			"import S;\n" +
     78 			"s : label=a[3] {System.out.println($label.y);} ;\n" +
     79 			"B : 'b' ;" + // defines B from inherited token space
     80 			"WS : (' '|'\\n') {skip();} ;\n" ;
     81 		String found = execParser("M.g", master, "MParser", "MLexer",
     82 								  "s", "b", debug);
     83 		assertEquals("S.a1000\n", found);
     84 	}
     85 
     86 	@Test public void testDelegatorInvokesDelegateRuleWithReturnStruct() throws Exception {
     87 		// must generate something like:
     88 		// public int a(int x) throws RecognitionException { return gS.a(x); }
     89 		// in M.
     90 		String slave =
     91 			"parser grammar S;\n" +
     92 			"a : B {System.out.print(\"S.a\");} ;\n";
     93 		mkdir(tmpdir);
     94 		writeFile(tmpdir, "S.g", slave);
     95 		String master =
     96 			"grammar M;\n" +
     97 			"import S;\n" +
     98 			"s : a {System.out.println($a.text);} ;\n" +
     99 			"B : 'b' ;" + // defines B from inherited token space
    100 			"WS : (' '|'\\n') {skip();} ;\n" ;
    101 		String found = execParser("M.g", master, "MParser", "MLexer",
    102 								  "s", "b", debug);
    103 		assertEquals("S.ab\n", found);
    104 	}
    105 
    106 	@Test public void testDelegatorAccessesDelegateMembers() throws Exception {
    107 		String slave =
    108 			"parser grammar S;\n" +
    109 			"@members {\n" +
    110 			"  public void foo() {System.out.println(\"foo\");}\n" +
    111 			"}\n" +
    112 			"a : B ;\n";
    113 		mkdir(tmpdir);
    114 		writeFile(tmpdir, "S.g", slave);
    115 		String master =
    116 			"grammar M;\n" +		// uses no rules from the import
    117 			"import S;\n" +
    118 			"s : 'b' {gS.foo();} ;\n" + // gS is import pointer
    119 			"WS : (' '|'\\n') {skip();} ;\n" ;
    120 		String found = execParser("M.g", master, "MParser", "MLexer",
    121 								  "s", "b", debug);
    122 		assertEquals("foo\n", found);
    123 	}
    124 
    125 	@Test public void testDelegatorInvokesFirstVersionOfDelegateRule() throws Exception {
    126 		String slave =
    127 			"parser grammar S;\n" +
    128 			"a : b {System.out.println(\"S.a\");} ;\n" +
    129 			"b : B ;\n" ;
    130 		mkdir(tmpdir);
    131 		writeFile(tmpdir, "S.g", slave);
    132 		String slave2 =
    133 			"parser grammar T;\n" +
    134 			"a : B {System.out.println(\"T.a\");} ;\n"; // hidden by S.a
    135 		writeFile(tmpdir, "T.g", slave2);
    136 		String master =
    137 			"grammar M;\n" +
    138 			"import S,T;\n" +
    139 			"s : a ;\n" +
    140 			"B : 'b' ;\n" +
    141 			"WS : (' '|'\\n') {skip();} ;\n" ;
    142 		String found = execParser("M.g", master, "MParser", "MLexer",
    143 								  "s", "b", debug);
    144 		assertEquals("S.a\n", found);
    145 	}
    146 
    147 	@Test public void testDelegatesSeeSameTokenType() throws Exception {
    148 		String slave =
    149 			"parser grammar S;\n" + // A, B, C token type order
    150 			"tokens { A; B; C; }\n" +
    151 			"x : A {System.out.println(\"S.x\");} ;\n";
    152 		mkdir(tmpdir);
    153 		writeFile(tmpdir, "S.g", slave);
    154 		String slave2 =
    155 			"parser grammar T;\n" +
    156 			"tokens { C; B; A; }\n" + // reverse order
    157 			"y : A {System.out.println(\"T.y\");} ;\n";
    158 		mkdir(tmpdir);
    159 		writeFile(tmpdir, "T.g", slave2);
    160 		// The lexer will create rules to match letters a, b, c.
    161 		// The associated token types A, B, C must have the same value
    162 		// and all import'd parsers.  Since ANTLR regenerates all imports
    163 		// for use with the delegator M, it can generate the same token type
    164 		// mapping in each parser:
    165 		// public static final int C=6;
    166 		// public static final int EOF=-1;
    167 		// public static final int B=5;
    168 		// public static final int WS=7;
    169 		// public static final int A=4;
    170 
    171 		String master =
    172 			"grammar M;\n" +
    173 			"import S,T;\n" +
    174 			"s : x y ;\n" + // matches AA, which should be "aa"
    175 			"B : 'b' ;\n" + // another order: B, A, C
    176 			"A : 'a' ;\n" +
    177 			"C : 'c' ;\n" +
    178 			"WS : (' '|'\\n') {skip();} ;\n" ;
    179 		String found = execParser("M.g", master, "MParser", "MLexer",
    180 								  "s", "aa", debug);
    181 		assertEquals("S.x\n" +
    182 					 "T.y\n", found);
    183 	}
    184 
    185 	@Test public void testDelegatesSeeSameTokenType2() throws Exception {
    186 		ErrorQueue equeue = new ErrorQueue();
    187 		ErrorManager.setErrorListener(equeue);
    188 		String slave =
    189 			"parser grammar S;\n" + // A, B, C token type order
    190 			"tokens { A; B; C; }\n" +
    191 			"x : A {System.out.println(\"S.x\");} ;\n";
    192 		mkdir(tmpdir);
    193 		writeFile(tmpdir, "S.g", slave);
    194 		String slave2 =
    195 			"parser grammar T;\n" +
    196 			"tokens { C; B; A; }\n" + // reverse order
    197 			"y : A {System.out.println(\"T.y\");} ;\n";
    198 		mkdir(tmpdir);
    199 		writeFile(tmpdir, "T.g", slave2);
    200 
    201 		String master =
    202 			"grammar M;\n" +
    203 			"import S,T;\n" +
    204 			"s : x y ;\n" + // matches AA, which should be "aa"
    205 			"B : 'b' ;\n" + // another order: B, A, C
    206 			"A : 'a' ;\n" +
    207 			"C : 'c' ;\n" +
    208 			"WS : (' '|'\\n') {skip();} ;\n" ;
    209 		writeFile(tmpdir, "M.g", master);
    210 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    211 		CompositeGrammar composite = new CompositeGrammar();
    212 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    213 		composite.setDelegationRoot(g);
    214 		g.parseAndBuildAST();
    215 		g.composite.assignTokenTypes();
    216 
    217 		String expectedTokenIDToTypeMap = "[A=4, B=5, C=6, WS=7]";
    218 		String expectedStringLiteralToTypeMap = "{}";
    219 		String expectedTypeToTokenList = "[A, B, C, WS]";
    220 
    221 		assertEquals(expectedTokenIDToTypeMap,
    222 					 realElements(g.composite.tokenIDToTypeMap).toString());
    223 		assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString());
    224 		assertEquals(expectedTypeToTokenList,
    225 					 realElements(g.composite.typeToTokenList).toString());
    226 
    227 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    228 	}
    229 
    230 	@Test public void testCombinedImportsCombined() throws Exception {
    231 		// for now, we don't allow combined to import combined
    232 		ErrorQueue equeue = new ErrorQueue();
    233 		ErrorManager.setErrorListener(equeue);
    234 		String slave =
    235 			"grammar S;\n" + // A, B, C token type order
    236 			"tokens { A; B; C; }\n" +
    237 			"x : 'x' INT {System.out.println(\"S.x\");} ;\n" +
    238 			"INT : '0'..'9'+ ;\n" +
    239 			"WS : (' '|'\\n') {skip();} ;\n";
    240 		mkdir(tmpdir);
    241 		writeFile(tmpdir, "S.g", slave);
    242 
    243 		String master =
    244 			"grammar M;\n" +
    245 			"import S;\n" +
    246 			"s : x INT ;\n";
    247 		writeFile(tmpdir, "M.g", master);
    248 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    249 		CompositeGrammar composite = new CompositeGrammar();
    250 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    251 		composite.setDelegationRoot(g);
    252 		g.parseAndBuildAST();
    253 		g.composite.assignTokenTypes();
    254 
    255 		assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size());
    256 		String expectedError = "error(161): "+tmpdir.toString().replaceFirst("\\-[0-9]+","")+"/M.g:2:8: combined grammar M cannot import combined grammar S";
    257 		assertEquals("unexpected errors: "+equeue, expectedError, equeue.errors.get(0).toString().replaceFirst("\\-[0-9]+",""));
    258 	}
    259 
    260 	@Test public void testSameStringTwoNames() throws Exception {
    261 		ErrorQueue equeue = new ErrorQueue();
    262 		ErrorManager.setErrorListener(equeue);
    263 		String slave =
    264 			"parser grammar S;\n" +
    265 			"tokens { A='a'; }\n" +
    266 			"x : A {System.out.println(\"S.x\");} ;\n";
    267 		mkdir(tmpdir);
    268 		writeFile(tmpdir, "S.g", slave);
    269 		String slave2 =
    270 			"parser grammar T;\n" +
    271 			"tokens { X='a'; }\n" +
    272 			"y : X {System.out.println(\"T.y\");} ;\n";
    273 		mkdir(tmpdir);
    274 		writeFile(tmpdir, "T.g", slave2);
    275 
    276 		String master =
    277 			"grammar M;\n" +
    278 			"import S,T;\n" +
    279 			"s : x y ;\n" +
    280 			"WS : (' '|'\\n') {skip();} ;\n" ;
    281 		writeFile(tmpdir, "M.g", master);
    282 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    283 		CompositeGrammar composite = new CompositeGrammar();
    284 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    285 		composite.setDelegationRoot(g);
    286 		g.parseAndBuildAST();
    287 		g.composite.assignTokenTypes();
    288 
    289 		String expectedTokenIDToTypeMap = "[A=4, WS=5, X=6]";
    290 		String expectedStringLiteralToTypeMap = "{'a'=4}";
    291 		String expectedTypeToTokenList = "[A, WS, X]";
    292 
    293 		assertEquals(expectedTokenIDToTypeMap,
    294 					 realElements(g.composite.tokenIDToTypeMap).toString());
    295 		assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString());
    296 		assertEquals(expectedTypeToTokenList,
    297 					 realElements(g.composite.typeToTokenList).toString());
    298 
    299 		Object expectedArg = "X='a'";
    300 		Object expectedArg2 = "A";
    301 		int expectedMsgID = ErrorManager.MSG_TOKEN_ALIAS_CONFLICT;
    302 		GrammarSemanticsMessage expectedMessage =
    303 			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
    304 		checkGrammarSemanticsError(equeue, expectedMessage);
    305 
    306 		assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size());
    307 
    308 		String expectedError =
    309 			"error(158): T.g:2:10: cannot alias X='a'; string already assigned to A";
    310 		assertEquals(expectedError, equeue.errors.get(0).toString());
    311 	}
    312 
    313 	@Test public void testSameNameTwoStrings() throws Exception {
    314 		ErrorQueue equeue = new ErrorQueue();
    315 		ErrorManager.setErrorListener(equeue);
    316 		String slave =
    317 			"parser grammar S;\n" +
    318 			"tokens { A='a'; }\n" +
    319 			"x : A {System.out.println(\"S.x\");} ;\n";
    320 		mkdir(tmpdir);
    321 		writeFile(tmpdir, "S.g", slave);
    322 		String slave2 =
    323 			"parser grammar T;\n" +
    324 			"tokens { A='x'; }\n" +
    325 			"y : A {System.out.println(\"T.y\");} ;\n";
    326 
    327 		writeFile(tmpdir, "T.g", slave2);
    328 
    329 		String master =
    330 			"grammar M;\n" +
    331 			"import S,T;\n" +
    332 			"s : x y ;\n" +
    333 			"WS : (' '|'\\n') {skip();} ;\n" ;
    334 		writeFile(tmpdir, "M.g", master);
    335 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    336 		CompositeGrammar composite = new CompositeGrammar();
    337 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    338 		composite.setDelegationRoot(g);
    339 		g.parseAndBuildAST();
    340 		g.composite.assignTokenTypes();
    341 
    342 		String expectedTokenIDToTypeMap = "[A=4, T__6=6, WS=5]";
    343 		String expectedStringLiteralToTypeMap = "{'a'=4, 'x'=6}";
    344 		String expectedTypeToTokenList = "[A, WS, T__6]";
    345 
    346 		assertEquals(expectedTokenIDToTypeMap,
    347 					 realElements(g.composite.tokenIDToTypeMap).toString());
    348 		assertEquals(expectedStringLiteralToTypeMap, sortMapToString(g.composite.stringLiteralToTypeMap));
    349 		assertEquals(expectedTypeToTokenList,
    350 					 realElements(g.composite.typeToTokenList).toString());
    351 
    352 		Object expectedArg = "A='x'";
    353 		Object expectedArg2 = "'a'";
    354 		int expectedMsgID = ErrorManager.MSG_TOKEN_ALIAS_REASSIGNMENT;
    355 		GrammarSemanticsMessage expectedMessage =
    356 			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
    357 		checkGrammarSemanticsError(equeue, expectedMessage);
    358 
    359 		assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size());
    360 
    361 		String expectedError =
    362 			"error(159): T.g:2:10: cannot alias A='x'; token name already assigned to 'a'";
    363 		assertEquals(expectedError, equeue.errors.get(0).toString());
    364 	}
    365 
    366 	@Test public void testImportedTokenVocabIgnoredWithWarning() throws Exception {
    367 		ErrorQueue equeue = new ErrorQueue();
    368 		ErrorManager.setErrorListener(equeue);
    369 		String slave =
    370 			"parser grammar S;\n" +
    371 			"options {tokenVocab=whatever;}\n" +
    372 			"tokens { A='a'; }\n" +
    373 			"x : A {System.out.println(\"S.x\");} ;\n";
    374 		mkdir(tmpdir);
    375 		writeFile(tmpdir, "S.g", slave);
    376 
    377 		String master =
    378 			"grammar M;\n" +
    379 			"import S;\n" +
    380 			"s : x ;\n" +
    381 			"WS : (' '|'\\n') {skip();} ;\n" ;
    382 		writeFile(tmpdir, "M.g", master);
    383 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    384 		CompositeGrammar composite = new CompositeGrammar();
    385 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    386 		composite.setDelegationRoot(g);
    387 		g.parseAndBuildAST();
    388 		g.composite.assignTokenTypes();
    389 
    390 		Object expectedArg = "S";
    391 		int expectedMsgID = ErrorManager.MSG_TOKEN_VOCAB_IN_DELEGATE;
    392 		GrammarSemanticsMessage expectedMessage =
    393 			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
    394 		checkGrammarSemanticsWarning(equeue, expectedMessage);
    395 
    396 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    397 		assertEquals("unexpected errors: "+equeue, 1, equeue.warnings.size());
    398 
    399 		String expectedError =
    400 			"warning(160): S.g:2:10: tokenVocab option ignored in imported grammar S";
    401 		assertEquals(expectedError, equeue.warnings.get(0).toString());
    402 	}
    403 
    404 	@Test public void testImportedTokenVocabWorksInRoot() throws Exception {
    405 		ErrorQueue equeue = new ErrorQueue();
    406 		ErrorManager.setErrorListener(equeue);
    407 		String slave =
    408 			"parser grammar S;\n" +
    409 			"tokens { A='a'; }\n" +
    410 			"x : A {System.out.println(\"S.x\");} ;\n";
    411 		mkdir(tmpdir);
    412 		writeFile(tmpdir, "S.g", slave);
    413 
    414 		String tokens =
    415 			"A=99\n";
    416 		writeFile(tmpdir, "Test.tokens", tokens);
    417 
    418 		String master =
    419 			"grammar M;\n" +
    420 			"options {tokenVocab=Test;}\n" +
    421 			"import S;\n" +
    422 			"s : x ;\n" +
    423 			"WS : (' '|'\\n') {skip();} ;\n" ;
    424 		writeFile(tmpdir, "M.g", master);
    425 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    426 		CompositeGrammar composite = new CompositeGrammar();
    427 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    428 		composite.setDelegationRoot(g);
    429 		g.parseAndBuildAST();
    430 		g.composite.assignTokenTypes();
    431 
    432 		String expectedTokenIDToTypeMap = "[A=99, WS=101]";
    433 		String expectedStringLiteralToTypeMap = "{'a'=100}";
    434 		String expectedTypeToTokenList = "[A, 'a', WS]";
    435 
    436 		assertEquals(expectedTokenIDToTypeMap,
    437 					 realElements(g.composite.tokenIDToTypeMap).toString());
    438 		assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString());
    439 		assertEquals(expectedTypeToTokenList,
    440 					 realElements(g.composite.typeToTokenList).toString());
    441 
    442 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    443 	}
    444 
    445 	@Test public void testSyntaxErrorsInImportsNotThrownOut() throws Exception {
    446 		ErrorQueue equeue = new ErrorQueue();
    447 		ErrorManager.setErrorListener(equeue);
    448 		String slave =
    449 			"parser grammar S;\n" +
    450 			"options {toke\n";
    451 		mkdir(tmpdir);
    452 		writeFile(tmpdir, "S.g", slave);
    453 
    454 		String master =
    455 			"grammar M;\n" +
    456 			"import S;\n" +
    457 			"s : x ;\n" +
    458 			"WS : (' '|'\\n') {skip();} ;\n" ;
    459 		writeFile(tmpdir, "M.g", master);
    460 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    461 		CompositeGrammar composite = new CompositeGrammar();
    462 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    463 		composite.setDelegationRoot(g);
    464 		g.parseAndBuildAST();
    465 		g.composite.assignTokenTypes();
    466 
    467 		// whole bunch of errors from bad S.g file
    468 		assertEquals("unexpected errors: "+equeue, 5, equeue.errors.size());
    469 	}
    470 
    471 	@Test public void testSyntaxErrorsInImportsNotThrownOut2() throws Exception {
    472 		ErrorQueue equeue = new ErrorQueue();
    473 		ErrorManager.setErrorListener(equeue);
    474 		String slave =
    475 			"parser grammar S;\n" +
    476 			": A {System.out.println(\"S.x\");} ;\n";
    477 		mkdir(tmpdir);
    478 		writeFile(tmpdir, "S.g", slave);
    479 
    480 		String master =
    481 			"grammar M;\n" +
    482 			"import S;\n" +
    483 			"s : x ;\n" +
    484 			"WS : (' '|'\\n') {skip();} ;\n" ;
    485 		writeFile(tmpdir, "M.g", master);
    486 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    487 		CompositeGrammar composite = new CompositeGrammar();
    488 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    489 		composite.setDelegationRoot(g);
    490 		g.parseAndBuildAST();
    491 		g.composite.assignTokenTypes();
    492 
    493 		// whole bunch of errors from bad S.g file
    494 		assertEquals("unexpected errors: "+equeue, 3, equeue.errors.size());
    495 	}
    496 
    497 	@Test public void testDelegatorRuleOverridesDelegate() throws Exception {
    498 		String slave =
    499 			"parser grammar S;\n" +
    500 			"a : b {System.out.println(\"S.a\");} ;\n" +
    501 			"b : B ;\n" ;
    502 		mkdir(tmpdir);
    503 		writeFile(tmpdir, "S.g", slave);
    504 		String master =
    505 			"grammar M;\n" +
    506 			"import S;\n" +
    507 			"b : 'b'|'c' ;\n" +
    508 			"WS : (' '|'\\n') {skip();} ;\n" ;
    509 		String found = execParser("M.g", master, "MParser", "MLexer",
    510 								  "a", "c", debug);
    511 		assertEquals("S.a\n", found);
    512 	}
    513 
    514 	@Test public void testDelegatorRuleOverridesLookaheadInDelegate() throws Exception {
    515 		String slave =
    516 			"parser grammar JavaDecl;\n" +
    517 			"type : 'int' ;\n" +
    518 			"decl : type ID ';'\n" +
    519 			"     | type ID init ';' {System.out.println(\"JavaDecl: \"+$decl.text);}\n" +
    520 			"     ;\n" +
    521 			"init : '=' INT ;\n" ;
    522 		mkdir(tmpdir);
    523 		writeFile(tmpdir, "JavaDecl.g", slave);
    524 		String master =
    525 			"grammar Java;\n" +
    526 			"import JavaDecl;\n" +
    527 			"prog : decl ;\n" +
    528 			"type : 'int' | 'float' ;\n" +
    529 			"\n" +
    530 			"ID  : 'a'..'z'+ ;\n" +
    531 			"INT : '0'..'9'+ ;\n" +
    532 			"WS : (' '|'\\n') {skip();} ;\n" ;
    533 		// for float to work in decl, type must be overridden
    534 		String found = execParser("Java.g", master, "JavaParser", "JavaLexer",
    535 								  "prog", "float x = 3;", debug);
    536 		assertEquals("JavaDecl: floatx=3;\n", found);
    537 	}
    538 
    539     @Test public void testDelegatorRuleOverridesDelegates() throws Exception {
    540         String slave =
    541             "parser grammar S;\n" +
    542             "a : b {System.out.println(\"S.a\");} ;\n" +
    543             "b : B ;\n" ;
    544         mkdir(tmpdir);
    545         writeFile(tmpdir, "S.g", slave);
    546 
    547         String slave2 =
    548             "parser grammar T;\n" +
    549             "tokens { A='x'; }\n" +
    550             "b : B {System.out.println(\"T.b\");} ;\n";
    551         writeFile(tmpdir, "T.g", slave2);
    552 
    553         String master =
    554             "grammar M;\n" +
    555             "import S, T;\n" +
    556             "b : 'b'|'c' {System.out.println(\"M.b\");}|B|A ;\n" +
    557             "WS : (' '|'\\n') {skip();} ;\n" ;
    558         String found = execParser("M.g", master, "MParser", "MLexer",
    559                                   "a", "c", debug);
    560         assertEquals("M.b\n" +
    561                      "S.a\n", found);
    562     }
    563 
    564 	// LEXER INHERITANCE
    565 
    566 	@Test public void testLexerDelegatorInvokesDelegateRule() throws Exception {
    567 		String slave =
    568 			"lexer grammar S;\n" +
    569 			"A : 'a' {System.out.println(\"S.A\");} ;\n" +
    570 			"C : 'c' ;\n";
    571 		mkdir(tmpdir);
    572 		writeFile(tmpdir, "S.g", slave);
    573 		String master =
    574 			"lexer grammar M;\n" +
    575 			"import S;\n" +
    576 			"B : 'b' ;\n" +
    577 			"WS : (' '|'\\n') {skip();} ;\n" ;
    578 		String found = execLexer("M.g", master, "M", "abc", debug);
    579 		assertEquals("S.A\nabc\n", found);
    580 	}
    581 
    582 	@Test public void testLexerDelegatorRuleOverridesDelegate() throws Exception {
    583 		String slave =
    584 			"lexer grammar S;\n" +
    585 			"A : 'a' {System.out.println(\"S.A\");} ;\n" +
    586 			"B : 'b' {System.out.println(\"S.B\");} ;\n";
    587 		mkdir(tmpdir);
    588 		writeFile(tmpdir, "S.g", slave);
    589 		String master =
    590 			"lexer grammar M;\n" +
    591 			"import S;\n" +
    592 			"A : 'a' B {System.out.println(\"M.A\");} ;\n" +
    593 			"WS : (' '|'\\n') {skip();} ;\n" ;
    594 		String found = execLexer("M.g", master, "M", "ab", debug);
    595 		assertEquals("S.B\n" +
    596 					 "M.A\n" +
    597 					 "ab\n", found);
    598 	}
    599 
    600 	@Test public void testLexerDelegatorRuleOverridesDelegateLeavingNoRules() throws Exception {
    601 		// M.Tokens has nothing to predict tokens from S.  Should
    602 		// not include S.Tokens alt in this case?
    603 		String slave =
    604 			"lexer grammar S;\n" +
    605 			"A : 'a' {System.out.println(\"S.A\");} ;\n";
    606 		mkdir(tmpdir);
    607 		writeFile(tmpdir, "S.g", slave);
    608 		String master =
    609 			"lexer grammar M;\n" +
    610 			"import S;\n" +
    611 			"A : 'a' {System.out.println(\"M.A\");} ;\n" +
    612 			"WS : (' '|'\\n') {skip();} ;\n" ;
    613 		writeFile(tmpdir, "/M.g", master);
    614 
    615 		ErrorQueue equeue = new ErrorQueue();
    616 		ErrorManager.setErrorListener(equeue);
    617 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    618 		CompositeGrammar composite = new CompositeGrammar();
    619 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    620 		composite.setDelegationRoot(g);
    621 		g.parseAndBuildAST();
    622 		composite.assignTokenTypes();
    623 		composite.defineGrammarSymbols();
    624 		composite.createNFAs();
    625 		g.createLookaheadDFAs(false);
    626 
    627 		// predict only alts from M not S
    628 		String expectingDFA =
    629 			".s0-'a'->.s1\n" +
    630 			".s0-{'\\n', ' '}->:s3=>2\n" +
    631 			".s1-<EOT>->:s2=>1\n";
    632 		org.antlr.analysis.DFA dfa = g.getLookaheadDFA(1);
    633 		FASerializer serializer = new FASerializer(g);
    634 		String result = serializer.serialize(dfa.startState);
    635 		assertEquals(expectingDFA, result);
    636 
    637 		// must not be a "unreachable alt: Tokens" error
    638 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    639 	}
    640 
    641 	@Test public void testInvalidImportMechanism() throws Exception {
    642 		// M.Tokens has nothing to predict tokens from S.  Should
    643 		// not include S.Tokens alt in this case?
    644 		String slave =
    645 			"lexer grammar S;\n" +
    646 			"A : 'a' {System.out.println(\"S.A\");} ;\n";
    647 		mkdir(tmpdir);
    648 		writeFile(tmpdir, "S.g", slave);
    649 		String master =
    650 			"tree grammar M;\n" +
    651 			"import S;\n" +
    652 			"a : A ;";
    653 		writeFile(tmpdir, "/M.g", master);
    654 
    655 		ErrorQueue equeue = new ErrorQueue();
    656 		ErrorManager.setErrorListener(equeue);
    657 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    658 		CompositeGrammar composite = new CompositeGrammar();
    659 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    660 		composite.setDelegationRoot(g);
    661 		g.parseAndBuildAST();
    662 
    663 		assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size());
    664 		assertEquals("unexpected errors: "+equeue, 0, equeue.warnings.size());
    665 
    666 		String expectedError =
    667 			"error(161): "+tmpdir.toString().replaceFirst("\\-[0-9]+","")+"/M.g:2:8: tree grammar M cannot import lexer grammar S";
    668 		assertEquals(expectedError, equeue.errors.get(0).toString().replaceFirst("\\-[0-9]+",""));
    669 	}
    670 
    671 	@Test public void testSyntacticPredicateRulesAreNotInherited() throws Exception {
    672 		// if this compiles, it means that synpred1_S is defined in S.java
    673 		// but not MParser.java.  MParser has its own synpred1_M which must
    674 		// be separate to compile.
    675 		String slave =
    676 			"parser grammar S;\n" +
    677 			"a : 'a' {System.out.println(\"S.a1\");}\n" +
    678 			"  | 'a' {System.out.println(\"S.a2\");}\n" +
    679 			"  ;\n" +
    680 			"b : 'x' | 'y' {;} ;\n"; // preds generated but not need in DFA here
    681 		mkdir(tmpdir);
    682 		writeFile(tmpdir, "S.g", slave);
    683 		String master =
    684 			"grammar M;\n" +
    685 			"options {backtrack=true;}\n" +
    686 			"import S;\n" +
    687 			"start : a b ;\n" +
    688 			"nonsense : 'q' | 'q' {;} ;" + // forces def of preds here in M
    689 			"WS : (' '|'\\n') {skip();} ;\n" ;
    690 		String found = execParser("M.g", master, "MParser", "MLexer",
    691 								  "start", "ax", debug);
    692 		assertEquals("S.a1\n", found);
    693 	}
    694 
    695 	@Test public void testKeywordVSIDGivesNoWarning() throws Exception {
    696 		ErrorQueue equeue = new ErrorQueue();
    697 		ErrorManager.setErrorListener(equeue);
    698 		String slave =
    699 			"lexer grammar S;\n" +
    700 			"A : 'abc' {System.out.println(\"S.A\");} ;\n" +
    701 			"ID : 'a'..'z'+ ;\n";
    702 		mkdir(tmpdir);
    703 		writeFile(tmpdir, "S.g", slave);
    704 		String master =
    705 			"grammar M;\n" +
    706 			"import S;\n" +
    707 			"a : A {System.out.println(\"M.a\");} ;\n" +
    708 			"WS : (' '|'\\n') {skip();} ;\n" ;
    709 		String found = execParser("M.g", master, "MParser", "MLexer",
    710 								  "a", "abc", debug);
    711 
    712 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    713 		assertEquals("unexpected warnings: "+equeue, 0, equeue.warnings.size());
    714 
    715 		assertEquals("S.A\nM.a\n", found);
    716 	}
    717 
    718 	@Test public void testWarningForUndefinedToken() throws Exception {
    719 		ErrorQueue equeue = new ErrorQueue();
    720 		ErrorManager.setErrorListener(equeue);
    721 		String slave =
    722 			"lexer grammar S;\n" +
    723 			"A : 'abc' {System.out.println(\"S.A\");} ;\n";
    724 		mkdir(tmpdir);
    725 		writeFile(tmpdir, "S.g", slave);
    726 		String master =
    727 			"grammar M;\n" +
    728 			"import S;\n" +
    729 			"a : ABC A {System.out.println(\"M.a\");} ;\n" +
    730 			"WS : (' '|'\\n') {skip();} ;\n" ;
    731 		// A is defined in S but M should still see it and not give warning.
    732 		// only problem is ABC.
    733 
    734 		rawGenerateAndBuildRecognizer("M.g", master, "MParser", "MLexer", debug);
    735 
    736 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    737 		assertEquals("unexpected warnings: "+equeue, 1, equeue.warnings.size());
    738 
    739 		String expectedError =
    740 			"warning(105): "+tmpdir.toString().replaceFirst("\\-[0-9]+","")+File.separator+"M.g:3:5: no lexer rule corresponding to token: ABC";
    741 		assertEquals(expectedError, equeue.warnings.get(0).toString().replaceFirst("\\-[0-9]+",""));
    742 	}
    743 
    744 	/** Make sure that M can import S that imports T. */
    745 	@Test public void test3LevelImport() throws Exception {
    746 		ErrorQueue equeue = new ErrorQueue();
    747 		ErrorManager.setErrorListener(equeue);
    748 		String slave =
    749 			"parser grammar T;\n" +
    750 			"a : T ;\n" ;
    751 		mkdir(tmpdir);
    752 		writeFile(tmpdir, "T.g", slave);
    753 		String slave2 =
    754 			"parser grammar S;\n" + // A, B, C token type order
    755 			"import T;\n" +
    756 			"a : S ;\n" ;
    757 		mkdir(tmpdir);
    758 		writeFile(tmpdir, "S.g", slave2);
    759 
    760 		String master =
    761 			"grammar M;\n" +
    762 			"import S;\n" +
    763 			"a : M ;\n" ;
    764 		writeFile(tmpdir, "M.g", master);
    765 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    766 		CompositeGrammar composite = new CompositeGrammar();
    767 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    768 		composite.setDelegationRoot(g);
    769 		g.parseAndBuildAST();
    770 		g.composite.assignTokenTypes();
    771 		g.composite.defineGrammarSymbols();
    772 
    773 		String expectedTokenIDToTypeMap = "[M=4, S=5, T=6]";
    774 		String expectedStringLiteralToTypeMap = "{}";
    775 		String expectedTypeToTokenList = "[M, S, T]";
    776 
    777 		assertEquals(expectedTokenIDToTypeMap,
    778 					 realElements(g.composite.tokenIDToTypeMap).toString());
    779 		assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString());
    780 		assertEquals(expectedTypeToTokenList,
    781 					 realElements(g.composite.typeToTokenList).toString());
    782 
    783 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    784 
    785 		boolean ok =
    786 			rawGenerateAndBuildRecognizer("M.g", master, "MParser", null, false);
    787 		boolean expecting = true; // should be ok
    788 		assertEquals(expecting, ok);
    789 	}
    790 
    791 	@Test public void testBigTreeOfImports() throws Exception {
    792 		ErrorQueue equeue = new ErrorQueue();
    793 		ErrorManager.setErrorListener(equeue);
    794 		String slave =
    795 			"parser grammar T;\n" +
    796 			"x : T ;\n" ;
    797 		mkdir(tmpdir);
    798 		writeFile(tmpdir, "T.g", slave);
    799 		slave =
    800 			"parser grammar S;\n" +
    801 			"import T;\n" +
    802 			"y : S ;\n" ;
    803 		mkdir(tmpdir);
    804 		writeFile(tmpdir, "S.g", slave);
    805 
    806 		slave =
    807 			"parser grammar C;\n" +
    808 			"i : C ;\n" ;
    809 		mkdir(tmpdir);
    810 		writeFile(tmpdir, "C.g", slave);
    811 		slave =
    812 			"parser grammar B;\n" +
    813 			"j : B ;\n" ;
    814 		mkdir(tmpdir);
    815 		writeFile(tmpdir, "B.g", slave);
    816 		slave =
    817 			"parser grammar A;\n" +
    818 			"import B,C;\n" +
    819 			"k : A ;\n" ;
    820 		mkdir(tmpdir);
    821 		writeFile(tmpdir, "A.g", slave);
    822 
    823 		String master =
    824 			"grammar M;\n" +
    825 			"import S,A;\n" +
    826 			"a : M ;\n" ;
    827 		writeFile(tmpdir, "M.g", master);
    828 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    829 		CompositeGrammar composite = new CompositeGrammar();
    830 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    831 		composite.setDelegationRoot(g);
    832 		g.parseAndBuildAST();
    833 		g.composite.assignTokenTypes();
    834 		g.composite.defineGrammarSymbols();
    835 
    836 		String expectedTokenIDToTypeMap = "[A=4, B=5, C=6, M=7, S=8, T=9]";
    837 		String expectedStringLiteralToTypeMap = "{}";
    838 		String expectedTypeToTokenList = "[A, B, C, M, S, T]";
    839 
    840 		assertEquals(expectedTokenIDToTypeMap,
    841 					 realElements(g.composite.tokenIDToTypeMap).toString());
    842 		assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString());
    843 		assertEquals(expectedTypeToTokenList,
    844 					 realElements(g.composite.typeToTokenList).toString());
    845 
    846 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    847 
    848 		boolean ok =
    849 			rawGenerateAndBuildRecognizer("M.g", master, "MParser", null, false);
    850 		boolean expecting = true; // should be ok
    851 		assertEquals(expecting, ok);
    852 	}
    853 
    854 	@Test public void testRulesVisibleThroughMultilevelImport() throws Exception {
    855 		ErrorQueue equeue = new ErrorQueue();
    856 		ErrorManager.setErrorListener(equeue);
    857 		String slave =
    858 			"parser grammar T;\n" +
    859 			"x : T ;\n" ;
    860 		mkdir(tmpdir);
    861 		writeFile(tmpdir, "T.g", slave);
    862 		String slave2 =
    863 			"parser grammar S;\n" + // A, B, C token type order
    864 			"import T;\n" +
    865 			"a : S ;\n" ;
    866 		mkdir(tmpdir);
    867 		writeFile(tmpdir, "S.g", slave2);
    868 
    869 		String master =
    870 			"grammar M;\n" +
    871 			"import S;\n" +
    872 			"a : M x ;\n" ; // x MUST BE VISIBLE TO M
    873 		writeFile(tmpdir, "M.g", master);
    874 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    875 		CompositeGrammar composite = new CompositeGrammar();
    876 		Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite);
    877 		composite.setDelegationRoot(g);
    878 		g.parseAndBuildAST();
    879 		g.composite.assignTokenTypes();
    880 		g.composite.defineGrammarSymbols();
    881 
    882 		String expectedTokenIDToTypeMap = "[M=4, S=5, T=6]";
    883 		String expectedStringLiteralToTypeMap = "{}";
    884 		String expectedTypeToTokenList = "[M, S, T]";
    885 
    886 		assertEquals(expectedTokenIDToTypeMap,
    887 					 realElements(g.composite.tokenIDToTypeMap).toString());
    888 		assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString());
    889 		assertEquals(expectedTypeToTokenList,
    890 					 realElements(g.composite.typeToTokenList).toString());
    891 
    892 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    893 	}
    894 
    895 	@Test public void testNestedComposite() throws Exception {
    896 		// Wasn't compiling. http://www.antlr.org/jira/browse/ANTLR-438
    897 		ErrorQueue equeue = new ErrorQueue();
    898 		ErrorManager.setErrorListener(equeue);
    899 		String gstr =
    900 			"lexer grammar L;\n" +
    901 			"T1: '1';\n" +
    902 			"T2: '2';\n" +
    903 			"T3: '3';\n" +
    904 			"T4: '4';\n" ;
    905 		mkdir(tmpdir);
    906 		writeFile(tmpdir, "L.g", gstr);
    907 		gstr =
    908 			"parser grammar G1;\n" +
    909 			"s: a | b;\n" +
    910 			"a: T1;\n" +
    911 			"b: T2;\n" ;
    912 		mkdir(tmpdir);
    913 		writeFile(tmpdir, "G1.g", gstr);
    914 
    915 		gstr =
    916 			"parser grammar G2;\n" +
    917 			"import G1;\n" +
    918 			"a: T3;\n" ;
    919 		mkdir(tmpdir);
    920 		writeFile(tmpdir, "G2.g", gstr);
    921 		String G3str =
    922 			"grammar G3;\n" +
    923 			"import G2;\n" +
    924 			"b: T4;\n" ;
    925 		mkdir(tmpdir);
    926 		writeFile(tmpdir, "G3.g", G3str);
    927 
    928 		Tool antlr = newTool(new String[] {"-lib", tmpdir});
    929 		CompositeGrammar composite = new CompositeGrammar();
    930 		Grammar g = new Grammar(antlr,tmpdir+"/G3.g",composite);
    931 		composite.setDelegationRoot(g);
    932 		g.parseAndBuildAST();
    933 		g.composite.assignTokenTypes();
    934 		g.composite.defineGrammarSymbols();
    935 
    936 		String expectedTokenIDToTypeMap = "[T1=4, T2=5, T3=6, T4=7]";
    937 		String expectedStringLiteralToTypeMap = "{}";
    938 		String expectedTypeToTokenList = "[T1, T2, T3, T4]";
    939 
    940 		assertEquals(expectedTokenIDToTypeMap,
    941 					 realElements(g.composite.tokenIDToTypeMap).toString());
    942 		assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString());
    943 		assertEquals(expectedTypeToTokenList,
    944 					 realElements(g.composite.typeToTokenList).toString());
    945 
    946 		assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
    947 
    948 		boolean ok =
    949 			rawGenerateAndBuildRecognizer("G3.g", G3str, "G3Parser", null, false);
    950 		boolean expecting = true; // should be ok
    951 		assertEquals(expecting, ok);
    952 	}
    953 
    954 	@Test public void testHeadersPropogatedCorrectlyToImportedGrammars() throws Exception {
    955 		String slave =
    956 			"parser grammar S;\n" +
    957 			"a : B {System.out.print(\"S.a\");} ;\n";
    958 		mkdir(tmpdir);
    959 		writeFile(tmpdir, "S.g", slave);
    960 		String master =
    961 			"grammar M;\n" +
    962 			"import S;\n" +
    963 			"@header{package mypackage;}\n" +
    964 			"@lexer::header{package mypackage;}\n" +
    965 			"s : a ;\n" +
    966 			"B : 'b' ;" + // defines B from inherited token space
    967 			"WS : (' '|'\\n') {skip();} ;\n" ;
    968 		boolean ok = antlr("M.g", "M.g", master, debug);
    969 		boolean expecting = true; // should be ok
    970 		assertEquals(expecting, ok);
    971 	}
    972 
    973 }