TestCompositeGrammars.java revision 324c4644fee44b9898524c09511bd33c3f12e2df
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 */
28package org.antlr.test;
29
30import org.antlr.Tool;
31import org.antlr.tool.*;
32import org.junit.Test;
33
34import java.io.File;
35
36public 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}