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.codegen.CodeGenerator;
32import org.antlr.tool.ErrorManager;
33import org.antlr.tool.Grammar;
34import org.antlr.tool.GrammarSemanticsMessage;
35import org.junit.Ignore;
36import org.junit.Test;
37
38public class TestRewriteAST extends BaseTest {
39	protected boolean debug = false;
40
41	@Test public void testDelete() throws Exception {
42		String grammar =
43			"grammar T;\n" +
44			"options {output=AST;}\n" +
45			"a : ID INT -> ;\n" +
46			"ID : 'a'..'z'+ ;\n" +
47			"INT : '0'..'9'+;\n" +
48			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
49		String found = execParser("T.g", grammar, "TParser", "TLexer",
50				    "a", "abc 34", debug);
51		assertEquals("", found);
52	}
53
54	@Test public void testSingleToken() throws Exception {
55		String grammar =
56			"grammar T;\n" +
57			"options {output=AST;}\n" +
58			"a : ID -> ID;\n" +
59			"ID : 'a'..'z'+ ;\n" +
60			"INT : '0'..'9'+;\n" +
61			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
62		String found = execParser("T.g", grammar, "TParser", "TLexer",
63				    "a", "abc", debug);
64		assertEquals("abc\n", found);
65	}
66
67	@Test public void testSingleTokenToNewNode() throws Exception {
68		String grammar =
69			"grammar T;\n" +
70			"options {output=AST;}\n" +
71			"a : ID -> ID[\"x\"];\n" +
72			"ID : 'a'..'z'+ ;\n" +
73			"INT : '0'..'9'+;\n" +
74			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
75		String found = execParser("T.g", grammar, "TParser", "TLexer",
76				    "a", "abc", debug);
77		assertEquals("x\n", found);
78	}
79
80	@Test public void testSingleTokenToNewNodeRoot() throws Exception {
81		String grammar =
82			"grammar T;\n" +
83			"options {output=AST;}\n" +
84			"a : ID -> ^(ID[\"x\"] INT);\n" +
85			"ID : 'a'..'z'+ ;\n" +
86			"INT : '0'..'9'+;\n" +
87			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
88		String found = execParser("T.g", grammar, "TParser", "TLexer",
89				    "a", "abc", debug);
90		assertEquals("(x INT)\n", found);
91	}
92
93	@Test public void testSingleTokenToNewNode2() throws Exception {
94		// Allow creation of new nodes w/o args.
95		String grammar =
96			"grammar TT;\n" +
97			"options {output=AST;}\n" +
98			"a : ID -> ID[ ];\n" +
99			"ID : 'a'..'z'+ ;\n" +
100			"INT : '0'..'9'+;\n" +
101			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
102		String found = execParser("TT.g", grammar, "TTParser", "TTLexer",
103				    "a", "abc", debug);
104		assertEquals("ID\n", found);
105	}
106
107	@Test public void testSingleCharLiteral() throws Exception {
108		String grammar =
109			"grammar T;\n" +
110			"options {output=AST;}\n" +
111			"a : 'c' -> 'c';\n" +
112			"ID : 'a'..'z'+ ;\n" +
113			"INT : '0'..'9'+;\n" +
114			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
115		String found = execParser("T.g", grammar, "TParser", "TLexer",
116				    "a", "c", debug);
117		assertEquals("c\n", found);
118	}
119
120	@Test public void testSingleStringLiteral() throws Exception {
121		String grammar =
122			"grammar T;\n" +
123			"options {output=AST;}\n" +
124			"a : 'ick' -> 'ick';\n" +
125			"ID : 'a'..'z'+ ;\n" +
126			"INT : '0'..'9'+;\n" +
127			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
128		String found = execParser("T.g", grammar, "TParser", "TLexer",
129				    "a", "ick", debug);
130		assertEquals("ick\n", found);
131	}
132
133	@Test public void testSingleRule() throws Exception {
134		String grammar =
135			"grammar T;\n" +
136			"options {output=AST;}\n" +
137			"a : b -> b;\n" +
138			"b : ID ;\n" +
139			"ID : 'a'..'z'+ ;\n" +
140			"INT : '0'..'9'+;\n" +
141			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
142		String found = execParser("T.g", grammar, "TParser", "TLexer",
143				    "a", "abc", debug);
144		assertEquals("abc\n", found);
145	}
146
147	@Test public void testReorderTokens() throws Exception {
148		String grammar =
149			"grammar T;\n" +
150			"options {output=AST;}\n" +
151			"a : ID INT -> INT ID;\n" +
152			"ID : 'a'..'z'+ ;\n" +
153			"INT : '0'..'9'+;\n" +
154			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
155		String found = execParser("T.g", grammar, "TParser", "TLexer",
156				    "a", "abc 34", debug);
157		assertEquals("34 abc\n", found);
158	}
159
160	@Test public void testReorderTokenAndRule() throws Exception {
161		String grammar =
162			"grammar T;\n" +
163			"options {output=AST;}\n" +
164			"a : b INT -> INT b;\n" +
165			"b : ID ;\n" +
166			"ID : 'a'..'z'+ ;\n" +
167			"INT : '0'..'9'+;\n" +
168			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
169		String found = execParser("T.g", grammar, "TParser", "TLexer",
170				    "a", "abc 34", debug);
171		assertEquals("34 abc\n", found);
172	}
173
174	@Test public void testTokenTree() throws Exception {
175		String grammar =
176			"grammar T;\n" +
177			"options {output=AST;}\n" +
178			"a : ID INT -> ^(INT ID);\n" +
179			"ID : 'a'..'z'+ ;\n" +
180			"INT : '0'..'9'+;\n" +
181			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
182		String found = execParser("T.g", grammar, "TParser", "TLexer",
183				    "a", "abc 34", debug);
184		assertEquals("(34 abc)\n", found);
185	}
186
187	@Test public void testTokenTreeAfterOtherStuff() throws Exception {
188		String grammar =
189			"grammar T;\n" +
190			"options {output=AST;}\n" +
191			"a : 'void' ID INT -> 'void' ^(INT ID);\n" +
192			"ID : 'a'..'z'+ ;\n" +
193			"INT : '0'..'9'+;\n" +
194			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
195		String found = execParser("T.g", grammar, "TParser", "TLexer",
196				    "a", "void abc 34", debug);
197		assertEquals("void (34 abc)\n", found);
198	}
199
200	@Test public void testNestedTokenTreeWithOuterLoop() throws Exception {
201		// verify that ID and INT both iterate over outer index variable
202		String grammar =
203			"grammar T;\n" +
204			"options {output=AST;}\n" +
205			"tokens {DUH;}\n" +
206			"a : ID INT ID INT -> ^( DUH ID ^( DUH INT) )+ ;\n" +
207			"ID : 'a'..'z'+ ;\n" +
208			"INT : '0'..'9'+;\n" +
209			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
210		String found = execParser("T.g", grammar, "TParser", "TLexer",
211				    "a", "a 1 b 2", debug);
212		assertEquals("(DUH a (DUH 1)) (DUH b (DUH 2))\n", found);
213	}
214
215	@Test public void testOptionalSingleToken() throws Exception {
216		String grammar =
217			"grammar T;\n" +
218			"options {output=AST;}\n" +
219			"a : ID -> ID? ;\n" +
220			"ID : 'a'..'z'+ ;\n" +
221			"INT : '0'..'9'+;\n" +
222			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
223		String found = execParser("T.g", grammar, "TParser", "TLexer",
224				    "a", "abc", debug);
225		assertEquals("abc\n", found);
226	}
227
228	@Test public void testClosureSingleToken() throws Exception {
229		String grammar =
230			"grammar T;\n" +
231			"options {output=AST;}\n" +
232			"a : ID ID -> ID* ;\n" +
233			"ID : 'a'..'z'+ ;\n" +
234			"INT : '0'..'9'+;\n" +
235			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
236		String found = execParser("T.g", grammar, "TParser", "TLexer",
237				    "a", "a b", debug);
238		assertEquals("a b\n", found);
239	}
240
241	@Test public void testPositiveClosureSingleToken() throws Exception {
242		String grammar =
243			"grammar T;\n" +
244			"options {output=AST;}\n" +
245			"a : ID ID -> ID+ ;\n" +
246			"ID : 'a'..'z'+ ;\n" +
247			"INT : '0'..'9'+;\n" +
248			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
249		String found = execParser("T.g", grammar, "TParser", "TLexer",
250				    "a", "a b", debug);
251		assertEquals("a b\n", found);
252	}
253
254	@Test public void testOptionalSingleRule() throws Exception {
255		String grammar =
256			"grammar T;\n" +
257			"options {output=AST;}\n" +
258			"a : b -> b?;\n" +
259			"b : ID ;\n" +
260			"ID : 'a'..'z'+ ;\n" +
261			"INT : '0'..'9'+;\n" +
262			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
263		String found = execParser("T.g", grammar, "TParser", "TLexer",
264				    "a", "abc", debug);
265		assertEquals("abc\n", found);
266	}
267
268	@Test public void testClosureSingleRule() throws Exception {
269		String grammar =
270			"grammar T;\n" +
271			"options {output=AST;}\n" +
272			"a : b b -> b*;\n" +
273			"b : ID ;\n" +
274			"ID : 'a'..'z'+ ;\n" +
275			"INT : '0'..'9'+;\n" +
276			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
277		String found = execParser("T.g", grammar, "TParser", "TLexer",
278				    "a", "a b", debug);
279		assertEquals("a b\n", found);
280	}
281
282	@Test public void testClosureOfLabel() throws Exception {
283		String grammar =
284			"grammar T;\n" +
285			"options {output=AST;}\n" +
286			"a : x+=b x+=b -> $x*;\n" +
287			"b : ID ;\n" +
288			"ID : 'a'..'z'+ ;\n" +
289			"INT : '0'..'9'+;\n" +
290			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
291		String found = execParser("T.g", grammar, "TParser", "TLexer",
292				    "a", "a b", debug);
293		assertEquals("a b\n", found);
294	}
295
296	@Test public void testOptionalLabelNoListLabel() throws Exception {
297		String grammar =
298			"grammar T;\n" +
299			"options {output=AST;}\n" +
300			"a : (x=ID)? -> $x?;\n" +
301			"ID : 'a'..'z'+ ;\n" +
302			"INT : '0'..'9'+;\n" +
303			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
304		String found = execParser("T.g", grammar, "TParser", "TLexer",
305				    "a", "a", debug);
306		assertEquals("a\n", found);
307	}
308
309	@Test public void testPositiveClosureSingleRule() throws Exception {
310		String grammar =
311			"grammar T;\n" +
312			"options {output=AST;}\n" +
313			"a : b b -> b+;\n" +
314			"b : ID ;\n" +
315			"ID : 'a'..'z'+ ;\n" +
316			"INT : '0'..'9'+;\n" +
317			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
318		String found = execParser("T.g", grammar, "TParser", "TLexer",
319				    "a", "a b", debug);
320		assertEquals("a b\n", found);
321	}
322
323	@Test public void testSinglePredicateT() throws Exception {
324		String grammar =
325			"grammar T;\n" +
326			"options {output=AST;}\n" +
327			"a : ID -> {true}? ID -> ;\n" +
328			"ID : 'a'..'z'+ ;\n" +
329			"INT : '0'..'9'+;\n" +
330			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
331		String found = execParser("T.g", grammar, "TParser", "TLexer",
332				    "a", "abc", debug);
333		assertEquals("abc\n", found);
334	}
335
336	@Test public void testSinglePredicateF() throws Exception {
337		String grammar =
338			"grammar T;\n" +
339			"options {output=AST;}\n" +
340			"a : ID -> {false}? ID -> ;\n" +
341			"ID : 'a'..'z'+ ;\n" +
342			"INT : '0'..'9'+;\n" +
343			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
344		String found = execParser("T.g", grammar, "TParser", "TLexer",
345				    "a", "abc", debug);
346		assertEquals("", found);
347	}
348
349	@Test public void testMultiplePredicate() throws Exception {
350		String grammar =
351			"grammar T;\n" +
352			"options {output=AST;}\n" +
353			"a : ID INT -> {false}? ID\n" +
354			"           -> {true}? INT\n" +
355			"           -> \n" +
356			"  ;\n" +
357			"ID : 'a'..'z'+ ;\n" +
358			"INT : '0'..'9'+;\n" +
359			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
360		String found = execParser("T.g", grammar, "TParser", "TLexer",
361				    "a", "a 2", debug);
362		assertEquals("2\n", found);
363	}
364
365	@Test public void testMultiplePredicateTrees() throws Exception {
366		String grammar =
367			"grammar T;\n" +
368			"options {output=AST;}\n" +
369			"a : ID INT -> {false}? ^(ID INT)\n" +
370			"           -> {true}? ^(INT ID)\n" +
371			"           -> ID\n" +
372			"  ;\n" +
373			"ID : 'a'..'z'+ ;\n" +
374			"INT : '0'..'9'+;\n" +
375			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
376		String found = execParser("T.g", grammar, "TParser", "TLexer",
377				    "a", "a 2", debug);
378		assertEquals("(2 a)\n", found);
379	}
380
381	@Test public void testSimpleTree() throws Exception {
382		String grammar =
383			"grammar T;\n" +
384			"options {output=AST;}\n" +
385			"a : op INT -> ^(op INT);\n" +
386			"op : '+'|'-' ;\n" +
387			"ID : 'a'..'z'+ ;\n" +
388			"INT : '0'..'9'+;\n" +
389			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
390		String found = execParser("T.g", grammar, "TParser", "TLexer",
391				    "a", "-34", debug);
392		assertEquals("(- 34)\n", found);
393	}
394
395	@Test public void testSimpleTree2() throws Exception {
396		String grammar =
397			"grammar T;\n" +
398			"options {output=AST;}\n" +
399			"a : op INT -> ^(INT op);\n" +
400			"op : '+'|'-' ;\n" +
401			"ID : 'a'..'z'+ ;\n" +
402			"INT : '0'..'9'+;\n" +
403			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
404		String found = execParser("T.g", grammar, "TParser", "TLexer",
405				    "a", "+ 34", debug);
406		assertEquals("(34 +)\n", found);
407	}
408
409
410	@Test public void testNestedTrees() throws Exception {
411		String grammar =
412			"grammar T;\n" +
413			"options {output=AST;}\n" +
414			"a : 'var' (ID ':' type ';')+ -> ^('var' ^(':' ID type)+) ;\n" +
415			"type : 'int' | 'float' ;\n" +
416			"ID : 'a'..'z'+ ;\n" +
417			"INT : '0'..'9'+;\n" +
418			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
419		String found = execParser("T.g", grammar, "TParser", "TLexer",
420				    "a", "var a:int; b:float;", debug);
421		assertEquals("(var (: a int) (: b float))\n", found);
422	}
423
424	@Test public void testImaginaryTokenCopy() throws Exception {
425		String grammar =
426			"grammar T;\n" +
427			"options {output=AST;}\n" +
428			"tokens {VAR;}\n" +
429			"a : ID (',' ID)*-> ^(VAR ID)+ ;\n" +
430			"type : 'int' | 'float' ;\n" +
431			"ID : 'a'..'z'+ ;\n" +
432			"INT : '0'..'9'+;\n" +
433			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
434		String found = execParser("T.g", grammar, "TParser", "TLexer",
435				    "a", "a,b,c", debug);
436		assertEquals("(VAR a) (VAR b) (VAR c)\n", found);
437	}
438
439	@Test public void testTokenUnreferencedOnLeftButDefined() throws Exception {
440		String grammar =
441			"grammar T;\n" +
442			"options {output=AST;}\n" +
443			"tokens {VAR;}\n" +
444			"a : b -> ID ;\n" +
445			"b : ID ;\n"+
446			"ID : 'a'..'z'+ ;\n" +
447			"INT : '0'..'9'+;\n" +
448			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
449		String found = execParser("T.g", grammar, "TParser", "TLexer",
450				    "a", "a", debug);
451		assertEquals("ID\n", found);
452	}
453
454	@Test public void testImaginaryTokenCopySetText() throws Exception {
455		String grammar =
456			"grammar T;\n" +
457			"options {output=AST;}\n" +
458			"tokens {VAR;}\n" +
459			"a : ID (',' ID)*-> ^(VAR[\"var\"] ID)+ ;\n" +
460			"type : 'int' | 'float' ;\n" +
461			"ID : 'a'..'z'+ ;\n" +
462			"INT : '0'..'9'+;\n" +
463			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
464		String found = execParser("T.g", grammar, "TParser", "TLexer",
465				    "a", "a,b,c", debug);
466		assertEquals("(var a) (var b) (var c)\n", found);
467	}
468
469	@Test public void testImaginaryTokenNoCopyFromToken() throws Exception {
470		String grammar =
471			"grammar T;\n" +
472			"options {output=AST;}\n" +
473			"tokens {BLOCK;}\n" +
474			"a : lc='{' ID+ '}' -> ^(BLOCK[$lc] ID+) ;\n" +
475			"type : 'int' | 'float' ;\n" +
476			"ID : 'a'..'z'+ ;\n" +
477			"INT : '0'..'9'+;\n" +
478			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
479		String found = execParser("T.g", grammar, "TParser", "TLexer",
480				    "a", "{a b c}", debug);
481		assertEquals("({ a b c)\n", found);
482	}
483
484	@Test public void testImaginaryTokenNoCopyFromTokenSetText() throws Exception {
485		String grammar =
486			"grammar T;\n" +
487			"options {output=AST;}\n" +
488			"tokens {BLOCK;}\n" +
489			"a : lc='{' ID+ '}' -> ^(BLOCK[$lc,\"block\"] ID+) ;\n" +
490			"type : 'int' | 'float' ;\n" +
491			"ID : 'a'..'z'+ ;\n" +
492			"INT : '0'..'9'+;\n" +
493			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
494		String found = execParser("T.g", grammar, "TParser", "TLexer",
495				    "a", "{a b c}", debug);
496		assertEquals("(block a b c)\n", found);
497	}
498
499	@Test public void testMixedRewriteAndAutoAST() throws Exception {
500		String grammar =
501			"grammar T;\n" +
502			"options {output=AST;}\n" +
503			"tokens {BLOCK;}\n" +
504			"a : b b^ ;\n" + // 2nd b matches only an INT; can make it root
505			"b : ID INT -> INT ID\n" +
506			"  | INT\n" +
507			"  ;\n" +
508			"ID : 'a'..'z'+ ;\n" +
509			"INT : '0'..'9'+;\n" +
510			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
511		String found = execParser("T.g", grammar, "TParser", "TLexer",
512				    "a", "a 1 2", debug);
513		assertEquals("(2 1 a)\n", found);
514	}
515
516	@Test public void testSubruleWithRewrite() throws Exception {
517		String grammar =
518			"grammar T;\n" +
519			"options {output=AST;}\n" +
520			"tokens {BLOCK;}\n" +
521			"a : b b ;\n" +
522			"b : (ID INT -> INT ID | INT INT -> INT+ )\n" +
523			"  ;\n" +
524			"ID : 'a'..'z'+ ;\n" +
525			"INT : '0'..'9'+;\n" +
526			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
527		String found = execParser("T.g", grammar, "TParser", "TLexer",
528				    "a", "a 1 2 3", debug);
529		assertEquals("1 a 2 3\n", found);
530	}
531
532	@Test public void testSubruleWithRewrite2() throws Exception {
533		String grammar =
534			"grammar T;\n" +
535			"options {output=AST;}\n" +
536			"tokens {TYPE;}\n" +
537			"a : b b ;\n" +
538			"b : 'int'\n" +
539			"    ( ID -> ^(TYPE 'int' ID)\n" +
540			"    | ID '=' INT -> ^(TYPE 'int' ID INT)\n" +
541			"    )\n" +
542			"    ';'\n" +
543			"  ;\n" +
544			"ID : 'a'..'z'+ ;\n" +
545			"INT : '0'..'9'+;\n" +
546			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
547		String found = execParser("T.g", grammar, "TParser", "TLexer",
548				    "a", "int a; int b=3;", debug);
549		assertEquals("(TYPE int a) (TYPE int b 3)\n", found);
550	}
551
552	@Test public void testNestedRewriteShutsOffAutoAST() throws Exception {
553		String grammar =
554			"grammar T;\n" +
555			"options {output=AST;}\n" +
556			"tokens {BLOCK;}\n" +
557			"a : b b ;\n" +
558			"b : ID ( ID (last=ID -> $last)+ ) ';'\n" + // get last ID
559			"  | INT\n" + // should still get auto AST construction
560			"  ;\n" +
561			"ID : 'a'..'z'+ ;\n" +
562			"INT : '0'..'9'+;\n" +
563			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
564		String found = execParser("T.g", grammar, "TParser", "TLexer",
565				    "a", "a b c d; 42", debug);
566		assertEquals("d 42\n", found);
567	}
568
569	@Test public void testRewriteActions() throws Exception {
570		String grammar =
571			"grammar T;\n" +
572			"options {output=AST;}\n" +
573			"a : atom -> ^({adaptor.create(INT,\"9\")} atom) ;\n" +
574			"atom : INT ;\n" +
575			"ID : 'a'..'z'+ ;\n" +
576			"INT : '0'..'9'+;\n" +
577			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
578		String found = execParser("T.g", grammar, "TParser", "TLexer",
579				    "a", "3", debug);
580		assertEquals("(9 3)\n", found);
581	}
582
583	@Test public void testRewriteActions2() throws Exception {
584		String grammar =
585			"grammar T;\n" +
586			"options {output=AST;}\n" +
587			"a : atom -> {adaptor.create(INT,\"9\")} atom ;\n" +
588			"atom : INT ;\n" +
589			"ID : 'a'..'z'+ ;\n" +
590			"INT : '0'..'9'+;\n" +
591			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
592		String found = execParser("T.g", grammar, "TParser", "TLexer",
593				    "a", "3", debug);
594		assertEquals("9 3\n", found);
595	}
596
597	@Test public void testRefToOldValue() throws Exception {
598		String grammar =
599			"grammar T;\n" +
600			"options {output=AST;}\n" +
601			"tokens {BLOCK;}\n" +
602			"a : (atom -> atom) (op='+' r=atom -> ^($op $a $r) )* ;\n" +
603			"atom : INT ;\n" +
604			"ID : 'a'..'z'+ ;\n" +
605			"INT : '0'..'9'+;\n" +
606			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
607		String found = execParser("T.g", grammar, "TParser", "TLexer",
608				    "a", "3+4+5", debug);
609		assertEquals("(+ (+ 3 4) 5)\n", found);
610	}
611
612	@Test public void testCopySemanticsForRules() throws Exception {
613		String grammar =
614			"grammar T;\n" +
615			"options {output=AST;}\n" +
616			"tokens {BLOCK;}\n" +
617			"a : atom -> ^(atom atom) ;\n" + // NOT CYCLE! (dup atom)
618			"atom : INT ;\n" +
619			"ID : 'a'..'z'+ ;\n" +
620			"INT : '0'..'9'+;\n" +
621			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
622		String found = execParser("T.g", grammar, "TParser", "TLexer",
623				    "a", "3", debug);
624		assertEquals("(3 3)\n", found);
625	}
626
627	@Test public void testCopySemanticsForRules2() throws Exception {
628		// copy type as a root for each invocation of (...)+ in rewrite
629		String grammar =
630			"grammar T;\n" +
631			"options {output=AST;}\n" +
632			"a : type ID (',' ID)* ';' -> ^(type ID)+ ;\n" +
633			"type : 'int' ;\n" +
634			"ID : 'a'..'z'+ ;\n" +
635			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
636		String found = execParser("T.g", grammar, "TParser", "TLexer",
637				    "a", "int a,b,c;", debug);
638		assertEquals("(int a) (int b) (int c)\n", found);
639	}
640
641	@Test public void testCopySemanticsForRules3() throws Exception {
642		// copy type *and* modifier even though it's optional
643		// for each invocation of (...)+ in rewrite
644		String grammar =
645			"grammar T;\n" +
646			"options {output=AST;}\n" +
647			"a : modifier? type ID (',' ID)* ';' -> ^(type modifier? ID)+ ;\n" +
648			"type : 'int' ;\n" +
649			"modifier : 'public' ;\n" +
650			"ID : 'a'..'z'+ ;\n" +
651			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
652		String found = execParser("T.g", grammar, "TParser", "TLexer",
653				    "a", "public int a,b,c;", debug);
654		assertEquals("(int public a) (int public b) (int public c)\n", found);
655	}
656
657	@Test public void testCopySemanticsForRules3Double() throws Exception {
658		// copy type *and* modifier even though it's optional
659		// for each invocation of (...)+ in rewrite
660		String grammar =
661			"grammar T;\n" +
662			"options {output=AST;}\n" +
663			"a : modifier? type ID (',' ID)* ';' -> ^(type modifier? ID)+ ^(type modifier? ID)+ ;\n" +
664			"type : 'int' ;\n" +
665			"modifier : 'public' ;\n" +
666			"ID : 'a'..'z'+ ;\n" +
667			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
668		String found = execParser("T.g", grammar, "TParser", "TLexer",
669				    "a", "public int a,b,c;", debug);
670		assertEquals("(int public a) (int public b) (int public c) (int public a) (int public b) (int public c)\n", found);
671	}
672
673	@Test public void testCopySemanticsForRules4() throws Exception {
674		// copy type *and* modifier even though it's optional
675		// for each invocation of (...)+ in rewrite
676		String grammar =
677			"grammar T;\n" +
678			"options {output=AST;}\n" +
679			"tokens {MOD;}\n" +
680			"a : modifier? type ID (',' ID)* ';' -> ^(type ^(MOD modifier)? ID)+ ;\n" +
681			"type : 'int' ;\n" +
682			"modifier : 'public' ;\n" +
683			"ID : 'a'..'z'+ ;\n" +
684			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
685		String found = execParser("T.g", grammar, "TParser", "TLexer",
686				    "a", "public int a,b,c;", debug);
687		assertEquals("(int (MOD public) a) (int (MOD public) b) (int (MOD public) c)\n", found);
688	}
689
690	@Test public void testCopySemanticsLists() throws Exception {
691		String grammar =
692			"grammar T;\n" +
693			"options {output=AST;}\n" +
694			"tokens {MOD;}\n" +
695			"a : ID (',' ID)* ';' -> ID+ ID+ ;\n"+
696			"ID : 'a'..'z'+ ;\n" +
697			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
698		String found = execParser("T.g", grammar, "TParser", "TLexer",
699				    "a", "a,b,c;", debug);
700		assertEquals("a b c a b c\n", found);
701	}
702
703	@Test public void testCopyRuleLabel() throws Exception {
704		String grammar =
705			"grammar T;\n" +
706			"options {output=AST;}\n" +
707			"tokens {BLOCK;}\n" +
708			"a : x=b -> $x $x;\n"+
709			"b : ID ;\n"+
710			"ID : 'a'..'z'+ ;\n" +
711			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
712		String found = execParser("T.g", grammar, "TParser", "TLexer",
713				    "a", "a", debug);
714		assertEquals("a a\n", found);
715	}
716
717	@Test public void testCopyRuleLabel2() throws Exception {
718		String grammar =
719			"grammar T;\n" +
720			"options {output=AST;}\n" +
721			"tokens {BLOCK;}\n" +
722			"a : x=b -> ^($x $x);\n"+
723			"b : ID ;\n"+
724			"ID : 'a'..'z'+ ;\n" +
725			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
726		String found = execParser("T.g", grammar, "TParser", "TLexer",
727				    "a", "a", debug);
728		assertEquals("(a a)\n", found);
729	}
730
731	@Test public void testQueueingOfTokens() throws Exception {
732		String grammar =
733			"grammar T;\n" +
734			"options {output=AST;}\n" +
735			"a : 'int' ID (',' ID)* ';' -> ^('int' ID+) ;\n" +
736			"op : '+'|'-' ;\n" +
737			"ID : 'a'..'z'+ ;\n" +
738			"INT : '0'..'9'+;\n" +
739			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
740		String found = execParser("T.g", grammar, "TParser", "TLexer",
741				    "a", "int a,b,c;", debug);
742		assertEquals("(int a b c)\n", found);
743	}
744
745	@Test public void testCopyOfTokens() throws Exception {
746		String grammar =
747			"grammar T;\n" +
748			"options {output=AST;}\n" +
749			"a : 'int' ID ';' -> 'int' ID 'int' ID ;\n" +
750			"op : '+'|'-' ;\n" +
751			"ID : 'a'..'z'+ ;\n" +
752			"INT : '0'..'9'+;\n" +
753			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
754		String found = execParser("T.g", grammar, "TParser", "TLexer",
755				    "a", "int a;", debug);
756		assertEquals("int a int a\n", found);
757	}
758
759	@Test public void testTokenCopyInLoop() throws Exception {
760		String grammar =
761			"grammar T;\n" +
762			"options {output=AST;}\n" +
763			"a : 'int' ID (',' ID)* ';' -> ^('int' ID)+ ;\n" +
764			"op : '+'|'-' ;\n" +
765			"ID : 'a'..'z'+ ;\n" +
766			"INT : '0'..'9'+;\n" +
767			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
768		String found = execParser("T.g", grammar, "TParser", "TLexer",
769				    "a", "int a,b,c;", debug);
770		assertEquals("(int a) (int b) (int c)\n", found);
771	}
772
773	@Test public void testTokenCopyInLoopAgainstTwoOthers() throws Exception {
774		// must smear 'int' copies across as root of multiple trees
775		String grammar =
776			"grammar T;\n" +
777			"options {output=AST;}\n" +
778			"a : 'int' ID ':' INT (',' ID ':' INT)* ';' -> ^('int' ID INT)+ ;\n" +
779			"op : '+'|'-' ;\n" +
780			"ID : 'a'..'z'+ ;\n" +
781			"INT : '0'..'9'+;\n" +
782			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
783		String found = execParser("T.g", grammar, "TParser", "TLexer",
784				    "a", "int a:1,b:2,c:3;", debug);
785		assertEquals("(int a 1) (int b 2) (int c 3)\n", found);
786	}
787
788	@Test public void testListRefdOneAtATime() throws Exception {
789		String grammar =
790			"grammar T;\n" +
791			"options {output=AST;}\n" +
792			"a : ID+ -> ID ID ID ;\n" + // works if 3 input IDs
793			"op : '+'|'-' ;\n" +
794			"ID : 'a'..'z'+ ;\n" +
795			"INT : '0'..'9'+;\n" +
796			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
797		String found = execParser("T.g", grammar, "TParser", "TLexer",
798				    "a", "a b c", debug);
799		assertEquals("a b c\n", found);
800	}
801
802	@Test public void testSplitListWithLabels() throws Exception {
803		String grammar =
804			"grammar T;\n" +
805			"options {output=AST;}\n" +
806			"tokens {VAR;}\n"+
807			"a : first=ID others+=ID* -> $first VAR $others+ ;\n" +
808			"op : '+'|'-' ;\n" +
809			"ID : 'a'..'z'+ ;\n" +
810			"INT : '0'..'9'+;\n" +
811			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
812		String found = execParser("T.g", grammar, "TParser", "TLexer",
813				    "a", "a b c", debug);
814		assertEquals("a VAR b c\n", found);
815	}
816
817	@Test public void testComplicatedMelange() throws Exception {
818		String grammar =
819			"grammar T;\n" +
820			"options {output=AST;}\n" +
821			"tokens {BLOCK;}\n" +
822			"a : A A b=B B b=B c+=C C c+=C D {String s=$D.text;} -> A+ B+ C+ D ;\n" +
823			"type : 'int' | 'float' ;\n" +
824			"A : 'a' ;\n" +
825			"B : 'b' ;\n" +
826			"C : 'c' ;\n" +
827			"D : 'd' ;\n" +
828			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
829		String found = execParser("T.g", grammar, "TParser", "TLexer",
830				    "a", "a a b b b c c c d", debug);
831		assertEquals("a a b b b c c c d\n", found);
832	}
833
834	@Test public void testRuleLabel() throws Exception {
835		String grammar =
836			"grammar T;\n" +
837			"options {output=AST;}\n" +
838			"tokens {BLOCK;}\n" +
839			"a : x=b -> $x;\n"+
840			"b : ID ;\n"+
841			"ID : 'a'..'z'+ ;\n" +
842			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
843		String found = execParser("T.g", grammar, "TParser", "TLexer",
844				    "a", "a", debug);
845		assertEquals("a\n", found);
846	}
847
848	@Test public void testAmbiguousRule() throws Exception {
849		String grammar =
850			"grammar T;\n" +
851			"options {output=AST;}\n" +
852			"a : ID a -> a | INT ;\n"+
853			"ID : 'a'..'z'+ ;\n" +
854			"INT: '0'..'9'+ ;\n" +
855			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
856		String found = execParser("T.g", grammar, "TParser", "TLexer",
857				    "a", "abc 34", debug);
858		assertEquals("34\n", found);
859	}
860
861	@Test public void testWeirdRuleRef() throws Exception {
862		ErrorQueue equeue = new ErrorQueue();
863		ErrorManager.setErrorListener(equeue);
864		String grammar =
865			"grammar T;\n" +
866			"options {output=AST;}\n" +
867			"a : ID a -> $a | INT ;\n"+
868			"ID : 'a'..'z'+ ;\n" +
869			"INT: '0'..'9'+ ;\n" +
870			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
871
872		Grammar g = new Grammar(grammar);
873		Tool antlr = newTool();
874		antlr.setOutputDirectory(null); // write to /dev/null
875		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
876		g.setCodeGenerator(generator);
877		generator.genRecognizer();
878
879		// $a is ambig; is it previous root or ref to a ref in alt?
880		assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size());
881	}
882
883	@Test public void testRuleListLabel() throws Exception {
884		String grammar =
885			"grammar T;\n" +
886			"options {output=AST;}\n" +
887			"tokens {BLOCK;}\n" +
888			"a : x+=b x+=b -> $x+;\n"+
889			"b : ID ;\n"+
890			"ID : 'a'..'z'+ ;\n" +
891			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
892		String found = execParser("T.g", grammar, "TParser", "TLexer",
893				    "a", "a b", debug);
894		assertEquals("a b\n", found);
895	}
896
897	@Test public void testRuleListLabel2() throws Exception {
898		String grammar =
899			"grammar T;\n" +
900			"options {output=AST;}\n" +
901			"tokens {BLOCK;}\n" +
902			"a : x+=b x+=b -> $x $x*;\n"+
903			"b : ID ;\n"+
904			"ID : 'a'..'z'+ ;\n" +
905			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
906		String found = execParser("T.g", grammar, "TParser", "TLexer",
907				    "a", "a b", debug);
908		assertEquals("a b\n", found);
909	}
910
911	@Test public void testOptional() throws Exception {
912		String grammar =
913			"grammar T;\n" +
914			"options {output=AST;}\n" +
915			"tokens {BLOCK;}\n" +
916			"a : x=b (y=b)? -> $x $y?;\n"+
917			"b : ID ;\n"+
918			"ID : 'a'..'z'+ ;\n" +
919			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
920		String found = execParser("T.g", grammar, "TParser", "TLexer",
921				    "a", "a", debug);
922		assertEquals("a\n", found);
923	}
924
925	@Test public void testOptional2() throws Exception {
926		String grammar =
927			"grammar T;\n" +
928			"options {output=AST;}\n" +
929			"tokens {BLOCK;}\n" +
930			"a : x=ID (y=b)? -> $x $y?;\n"+
931			"b : ID ;\n"+
932			"ID : 'a'..'z'+ ;\n" +
933			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
934		String found = execParser("T.g", grammar, "TParser", "TLexer",
935				    "a", "a b", debug);
936		assertEquals("a b\n", found);
937	}
938
939	@Test public void testOptional3() throws Exception {
940		String grammar =
941			"grammar T;\n" +
942			"options {output=AST;}\n" +
943			"tokens {BLOCK;}\n" +
944			"a : x=ID (y=b)? -> ($x $y)?;\n"+
945			"b : ID ;\n"+
946			"ID : 'a'..'z'+ ;\n" +
947			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
948		String found = execParser("T.g", grammar, "TParser", "TLexer",
949				    "a", "a b", debug);
950		assertEquals("a b\n", found);
951	}
952
953	@Test public void testOptional4() throws Exception {
954		String grammar =
955			"grammar T;\n" +
956			"options {output=AST;}\n" +
957			"tokens {BLOCK;}\n" +
958			"a : x+=ID (y=b)? -> ($x $y)?;\n"+
959			"b : ID ;\n"+
960			"ID : 'a'..'z'+ ;\n" +
961			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
962		String found = execParser("T.g", grammar, "TParser", "TLexer",
963				    "a", "a b", debug);
964		assertEquals("a b\n", found);
965	}
966
967	@Test public void testOptional5() throws Exception {
968		String grammar =
969			"grammar T;\n" +
970			"options {output=AST;}\n" +
971			"tokens {BLOCK;}\n" +
972			"a : ID -> ID? ;\n"+ // match an ID to optional ID
973			"b : ID ;\n"+
974			"ID : 'a'..'z'+ ;\n" +
975			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
976		String found = execParser("T.g", grammar, "TParser", "TLexer",
977				    "a", "a", debug);
978		assertEquals("a\n", found);
979	}
980
981	@Test public void testArbitraryExprType() throws Exception {
982		String grammar =
983			"grammar T;\n" +
984			"options {output=AST;}\n" +
985			"tokens {BLOCK;}\n" +
986			"a : x+=b x+=b -> {new CommonTree()};\n"+
987			"b : ID ;\n"+
988			"ID : 'a'..'z'+ ;\n" +
989			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
990		String found = execParser("T.g", grammar, "TParser", "TLexer",
991				    "a", "a b", debug);
992		assertEquals("", found);
993	}
994
995	@Test public void testSet() throws Exception {
996		String grammar =
997			"grammar T;\n" +
998			"options { output = AST; } \n" +
999			"a: (INT|ID)+ -> INT+ ID+ ;\n" +
1000			"INT: '0'..'9'+;\n" +
1001			"ID : 'a'..'z'+;\n" +
1002			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1003		String found = execParser("T.g", grammar, "TParser", "TLexer",
1004				    "a", "2 a 34 de", debug);
1005		assertEquals("2 34 a de\n", found);
1006	}
1007
1008	@Test public void testSet2() throws Exception {
1009		String grammar =
1010			"grammar T;\n" +
1011			"options { output = AST; } \n" +
1012			"a: (INT|ID) -> INT? ID? ;\n" +
1013			"INT: '0'..'9'+;\n" +
1014			"ID : 'a'..'z'+;\n" +
1015			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1016		String found = execParser("T.g", grammar, "TParser", "TLexer",
1017				    "a", "2", debug);
1018		assertEquals("2\n", found);
1019	}
1020
1021	@Ignore
1022    // TODO: FAILS. The should probably generate a warning from antlr
1023    // See http://www.antlr.org:8888/browse/ANTLR-162
1024    //
1025    public void testSetWithLabel() throws Exception {
1026
1027		String grammar =
1028			"grammar T;\n" +
1029			"options { output = AST; } \n" +
1030			"a : x=(INT|ID) -> $x ;\n" +
1031			"INT: '0'..'9'+;\n" +
1032			"ID : 'a'..'z'+;\n" +
1033			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1034		String found = execParser("T.g", grammar, "TParser", "TLexer",
1035				    "a", "2", debug);
1036		assertEquals("2\n", found);
1037	}
1038
1039	@Test public void testRewriteAction() throws Exception {
1040		String grammar =
1041			"grammar T; \n" +
1042			"options { output = AST; }\n" +
1043			"tokens { FLOAT; }\n" +
1044			"r\n" +
1045			"    : INT -> {new CommonTree(new CommonToken(FLOAT,$INT.text+\".0\"))} \n" +
1046			"    ; \n" +
1047			"INT : '0'..'9'+; \n" +
1048			"WS: (' ' | '\\n' | '\\t')+ {$channel = HIDDEN;}; \n";
1049		String found = execParser("T.g", grammar, "TParser", "TLexer",
1050				    "r", "25", debug);
1051		assertEquals("25.0\n", found);
1052	}
1053
1054	@Test public void testOptionalSubruleWithoutRealElements() throws Exception {
1055		// copy type *and* modifier even though it's optional
1056		// for each invocation of (...)+ in rewrite
1057		String grammar =
1058			"grammar T;\n" +
1059			"options {output=AST;} \n" +
1060			"tokens {PARMS;} \n" +
1061			"\n" +
1062			"modulo \n" +
1063			" : 'modulo' ID ('(' parms+ ')')? -> ^('modulo' ID ^(PARMS parms+)?) \n" +
1064			" ; \n" +
1065			"parms : '#'|ID; \n" +
1066			"ID : ('a'..'z' | 'A'..'Z')+;\n" +
1067			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1068		String found = execParser("T.g", grammar, "TParser", "TLexer",
1069				    "modulo", "modulo abc (x y #)", debug);
1070		assertEquals("(modulo abc (PARMS x y #))\n", found);
1071	}
1072
1073	// C A R D I N A L I T Y  I S S U E S
1074
1075	@Test public void testCardinality() throws Exception {
1076		String grammar =
1077			"grammar T;\n" +
1078			"options {output=AST;}\n" +
1079			"tokens {BLOCK;}\n" +
1080			"a : ID ID INT INT INT -> (ID INT)+;\n"+
1081			"ID : 'a'..'z'+ ;\n" +
1082			"INT : '0'..'9'+; \n" +
1083			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1084		execParser("T.g", grammar, "TParser", "TLexer",
1085				    "a", "a b 3 4 5", debug);
1086		String expecting =
1087			"org.antlr.runtime.tree.RewriteCardinalityException: token ID";
1088		String found = getFirstLineOfException();
1089		assertEquals(expecting, found);
1090	}
1091
1092	@Test public void testCardinality2() throws Exception {
1093		String grammar =
1094			"grammar T;\n" +
1095			"options {output=AST;}\n" +
1096			"a : ID+ -> ID ID ID ;\n" + // only 2 input IDs
1097			"op : '+'|'-' ;\n" +
1098			"ID : 'a'..'z'+ ;\n" +
1099			"INT : '0'..'9'+;\n" +
1100			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1101		execParser("T.g", grammar, "TParser", "TLexer",
1102				   "a", "a b", debug);
1103		String expecting =
1104			"org.antlr.runtime.tree.RewriteCardinalityException: token ID";
1105		String found = getFirstLineOfException();
1106		assertEquals(expecting, found);
1107	}
1108
1109	@Test public void testCardinality3() throws Exception {
1110		String grammar =
1111			"grammar T;\n" +
1112			"options {output=AST;}\n" +
1113			"a : ID? INT -> ID INT ;\n" +
1114			"op : '+'|'-' ;\n" +
1115			"ID : 'a'..'z'+ ;\n" +
1116			"INT : '0'..'9'+;\n" +
1117			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1118		execParser("T.g", grammar, "TParser", "TLexer",
1119				   "a", "3", debug);
1120		String expecting =
1121			"org.antlr.runtime.tree.RewriteEmptyStreamException: token ID";
1122		String found = getFirstLineOfException();
1123		assertEquals(expecting, found);
1124	}
1125
1126	@Test public void testLoopCardinality() throws Exception {
1127		String grammar =
1128			"grammar T;\n" +
1129			"options {output=AST;}\n" +
1130			"a : ID? INT -> ID+ INT ;\n" +
1131			"op : '+'|'-' ;\n" +
1132			"ID : 'a'..'z'+ ;\n" +
1133			"INT : '0'..'9'+;\n" +
1134			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1135		execParser("T.g", grammar, "TParser", "TLexer",
1136				   "a", "3", debug);
1137		String expecting =
1138			"org.antlr.runtime.tree.RewriteEarlyExitException";
1139		String found = getFirstLineOfException();
1140		assertEquals(expecting, found);
1141	}
1142
1143	@Test public void testWildcard() throws Exception {
1144		String grammar =
1145			"grammar T;\n" +
1146			"options {output=AST;}\n" +
1147			"a : ID c=. -> $c;\n" +
1148			"ID : 'a'..'z'+ ;\n" +
1149			"INT : '0'..'9'+;\n" +
1150			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1151		String found = execParser("T.g", grammar, "TParser", "TLexer",
1152				    "a", "abc 34", debug);
1153		assertEquals("34\n", found);
1154	}
1155
1156	// E R R O R S
1157
1158	@Test public void testUnknownRule() throws Exception {
1159		ErrorQueue equeue = new ErrorQueue();
1160		ErrorManager.setErrorListener(equeue);
1161
1162		String grammar =
1163			"grammar T;\n" +
1164			"options {output=AST;}\n" +
1165			"a : INT -> ugh ;\n" +
1166			"ID : 'a'..'z'+ ;\n" +
1167			"INT : '0'..'9'+;\n" +
1168			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1169
1170		Grammar g = new Grammar(grammar);
1171		Tool antlr = newTool();
1172		antlr.setOutputDirectory(null); // write to /dev/null
1173		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
1174		g.setCodeGenerator(generator);
1175		generator.genRecognizer();
1176
1177		int expectedMsgID = ErrorManager.MSG_UNDEFINED_RULE_REF;
1178		Object expectedArg = "ugh";
1179		Object expectedArg2 = null;
1180		GrammarSemanticsMessage expectedMessage =
1181			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
1182
1183		checkError(equeue, expectedMessage);
1184	}
1185
1186	@Test public void testKnownRuleButNotInLHS() throws Exception {
1187		ErrorQueue equeue = new ErrorQueue();
1188		ErrorManager.setErrorListener(equeue);
1189
1190		String grammar =
1191			"grammar T;\n" +
1192			"options {output=AST;}\n" +
1193			"a : INT -> b ;\n" +
1194			"b : 'b' ;\n" +
1195			"ID : 'a'..'z'+ ;\n" +
1196			"INT : '0'..'9'+;\n" +
1197			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1198
1199		Grammar g = new Grammar(grammar);
1200		Tool antlr = newTool();
1201		antlr.setOutputDirectory(null); // write to /dev/null
1202		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
1203		g.setCodeGenerator(generator);
1204		generator.genRecognizer();
1205
1206		int expectedMsgID = ErrorManager.MSG_REWRITE_ELEMENT_NOT_PRESENT_ON_LHS;
1207		Object expectedArg = "b";
1208		Object expectedArg2 = null;
1209		GrammarSemanticsMessage expectedMessage =
1210			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
1211
1212		checkError(equeue, expectedMessage);
1213	}
1214
1215	@Test public void testUnknownToken() throws Exception {
1216		ErrorQueue equeue = new ErrorQueue();
1217		ErrorManager.setErrorListener(equeue);
1218
1219		String grammar =
1220			"grammar T;\n" +
1221			"options {output=AST;}\n" +
1222			"a : INT -> ICK ;\n" +
1223			"ID : 'a'..'z'+ ;\n" +
1224			"INT : '0'..'9'+;\n" +
1225			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1226
1227		Grammar g = new Grammar(grammar);
1228		Tool antlr = newTool();
1229		antlr.setOutputDirectory(null); // write to /dev/null
1230		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
1231		g.setCodeGenerator(generator);
1232		generator.genRecognizer();
1233
1234		int expectedMsgID = ErrorManager.MSG_UNDEFINED_TOKEN_REF_IN_REWRITE;
1235		Object expectedArg = "ICK";
1236		Object expectedArg2 = null;
1237		GrammarSemanticsMessage expectedMessage =
1238			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
1239
1240		checkError(equeue, expectedMessage);
1241	}
1242
1243	@Test public void testUnknownLabel() throws Exception {
1244		ErrorQueue equeue = new ErrorQueue();
1245		ErrorManager.setErrorListener(equeue);
1246
1247		String grammar =
1248			"grammar T;\n" +
1249			"options {output=AST;}\n" +
1250			"a : INT -> $foo ;\n" +
1251			"ID : 'a'..'z'+ ;\n" +
1252			"INT : '0'..'9'+;\n" +
1253			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1254
1255		Grammar g = new Grammar(grammar);
1256		Tool antlr = newTool();
1257		antlr.setOutputDirectory(null); // write to /dev/null
1258		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
1259		g.setCodeGenerator(generator);
1260		generator.genRecognizer();
1261
1262		int expectedMsgID = ErrorManager.MSG_UNDEFINED_LABEL_REF_IN_REWRITE;
1263		Object expectedArg = "foo";
1264		Object expectedArg2 = null;
1265		GrammarSemanticsMessage expectedMessage =
1266			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
1267
1268		checkError(equeue, expectedMessage);
1269	}
1270
1271	@Test public void testUnknownCharLiteralToken() throws Exception {
1272		ErrorQueue equeue = new ErrorQueue();
1273		ErrorManager.setErrorListener(equeue);
1274
1275		String grammar =
1276			"grammar T;\n" +
1277			"options {output=AST;}\n" +
1278			"a : INT -> 'a' ;\n" +
1279			"ID : 'a'..'z'+ ;\n" +
1280			"INT : '0'..'9'+;\n" +
1281			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1282
1283		Grammar g = new Grammar(grammar);
1284		Tool antlr = newTool();
1285		antlr.setOutputDirectory(null); // write to /dev/null
1286		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
1287		g.setCodeGenerator(generator);
1288		generator.genRecognizer();
1289
1290		int expectedMsgID = ErrorManager.MSG_UNDEFINED_TOKEN_REF_IN_REWRITE;
1291		Object expectedArg = "'a'";
1292		Object expectedArg2 = null;
1293		GrammarSemanticsMessage expectedMessage =
1294			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
1295
1296		checkError(equeue, expectedMessage);
1297	}
1298
1299	@Test public void testUnknownStringLiteralToken() throws Exception {
1300		ErrorQueue equeue = new ErrorQueue();
1301		ErrorManager.setErrorListener(equeue);
1302
1303		String grammar =
1304			"grammar T;\n" +
1305			"options {output=AST;}\n" +
1306			"a : INT -> 'foo' ;\n" +
1307			"ID : 'a'..'z'+ ;\n" +
1308			"INT : '0'..'9'+;\n" +
1309			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1310
1311		Grammar g = new Grammar(grammar);
1312		Tool antlr = newTool();
1313		antlr.setOutputDirectory(null); // write to /dev/null
1314		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
1315		g.setCodeGenerator(generator);
1316		generator.genRecognizer();
1317
1318		int expectedMsgID = ErrorManager.MSG_UNDEFINED_TOKEN_REF_IN_REWRITE;
1319		Object expectedArg = "'foo'";
1320		Object expectedArg2 = null;
1321		GrammarSemanticsMessage expectedMessage =
1322			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2);
1323
1324		checkError(equeue, expectedMessage);
1325	}
1326
1327	@Test public void testExtraTokenInSimpleDecl() throws Exception {
1328		String grammar =
1329			"grammar foo;\n" +
1330			"options {output=AST;}\n" +
1331			"tokens {EXPR;}\n" +
1332			"decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;\n" +
1333			"type : 'int' | 'float' ;\n" +
1334			"ID : 'a'..'z'+ ;\n" +
1335			"INT : '0'..'9'+;\n" +
1336			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1337		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1338								  "decl", "int 34 x=1;", debug);
1339		assertEquals("line 1:4 extraneous input '34' expecting ID\n", this.stderrDuringParse);
1340		assertEquals("(EXPR int x 1)\n", found); // tree gets correct x and 1 tokens
1341	}
1342
1343	@Test public void testMissingIDInSimpleDecl() throws Exception {
1344		String grammar =
1345			"grammar foo;\n" +
1346			"options {output=AST;}\n" +
1347			"tokens {EXPR;}\n" +
1348			"decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;\n" +
1349			"type : 'int' | 'float' ;\n" +
1350			"ID : 'a'..'z'+ ;\n" +
1351			"INT : '0'..'9'+;\n" +
1352			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1353		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1354								  "decl", "int =1;", debug);
1355		assertEquals("line 1:4 missing ID at '='\n", this.stderrDuringParse);
1356		assertEquals("(EXPR int <missing ID> 1)\n", found); // tree gets invented ID token
1357	}
1358
1359	@Test public void testMissingSetInSimpleDecl() throws Exception {
1360		String grammar =
1361			"grammar foo;\n" +
1362			"options {output=AST;}\n" +
1363			"tokens {EXPR;}\n" +
1364			"decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;\n" +
1365			"type : 'int' | 'float' ;\n" +
1366			"ID : 'a'..'z'+ ;\n" +
1367			"INT : '0'..'9'+;\n" +
1368			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1369		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1370								  "decl", "x=1;", debug);
1371		assertEquals("line 1:0 mismatched input 'x' expecting set null\n", this.stderrDuringParse);
1372		assertEquals("(EXPR <error: x> x 1)\n", found); // tree gets invented ID token
1373	}
1374
1375	@Test public void testMissingTokenGivesErrorNode() throws Exception {
1376		String grammar =
1377			"grammar foo;\n" +
1378			"options {output=AST;}\n" +
1379			"a : ID INT -> ID INT ;\n" +
1380			"ID : 'a'..'z'+ ;\n" +
1381			"INT : '0'..'9'+;\n" +
1382			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1383		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1384								  "a", "abc", debug);
1385		assertEquals("line 1:3 missing INT at '<EOF>'\n", this.stderrDuringParse);
1386		// doesn't do in-line recovery for sets (yet?)
1387		assertEquals("abc <missing INT>\n", found);
1388	}
1389
1390	@Test public void testExtraTokenGivesErrorNode() throws Exception {
1391		String grammar =
1392			"grammar foo;\n" +
1393			"options {output=AST;}\n" +
1394			"a : b c -> b c;\n" +
1395			"b : ID -> ID ;\n" +
1396			"c : INT -> INT ;\n" +
1397			"ID : 'a'..'z'+ ;\n" +
1398			"INT : '0'..'9'+;\n" +
1399			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1400		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1401								  "a", "abc ick 34", debug);
1402		assertEquals("line 1:4 extraneous input 'ick' expecting INT\n", this.stderrDuringParse);
1403		assertEquals("abc 34\n", found);
1404	}
1405
1406	@Test public void testMissingFirstTokenGivesErrorNode() throws Exception {
1407		String grammar =
1408			"grammar foo;\n" +
1409			"options {output=AST;}\n" +
1410			"a : ID INT -> ID INT ;\n" +
1411			"ID : 'a'..'z'+ ;\n" +
1412			"INT : '0'..'9'+;\n" +
1413			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1414		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1415								  "a", "34", debug);
1416		assertEquals("line 1:0 missing ID at '34'\n", this.stderrDuringParse);
1417		assertEquals("<missing ID> 34\n", found);
1418	}
1419
1420	@Test public void testMissingFirstTokenGivesErrorNode2() throws Exception {
1421		String grammar =
1422			"grammar foo;\n" +
1423			"options {output=AST;}\n" +
1424			"a : b c -> b c;\n" +
1425			"b : ID -> ID ;\n" +
1426			"c : INT -> INT ;\n" +
1427			"ID : 'a'..'z'+ ;\n" +
1428			"INT : '0'..'9'+;\n" +
1429			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1430		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1431								  "a", "34", debug);
1432		// finds an error at the first token, 34, and re-syncs.
1433		// re-synchronizing does not consume a token because 34 follows
1434		// ref to rule b (start of c). It then matches 34 in c.
1435		assertEquals("line 1:0 missing ID at '34'\n", this.stderrDuringParse);
1436		assertEquals("<missing ID> 34\n", found);
1437	}
1438
1439	@Test public void testNoViableAltGivesErrorNode() throws Exception {
1440		String grammar =
1441			"grammar foo;\n" +
1442			"options {output=AST;}\n" +
1443			"a : b -> b | c -> c;\n" +
1444			"b : ID -> ID ;\n" +
1445			"c : INT -> INT ;\n" +
1446			"ID : 'a'..'z'+ ;\n" +
1447			"S : '*' ;\n" +
1448			"INT : '0'..'9'+;\n" +
1449			"WS : (' '|'\\n') {$channel=HIDDEN;} ;\n";
1450		String found = execParser("foo.g", grammar, "fooParser", "fooLexer",
1451								  "a", "*", debug);
1452		// finds an error at the first token, 34, and re-syncs.
1453		// re-synchronizing does not consume a token because 34 follows
1454		// ref to rule b (start of c). It then matches 34 in c.
1455		assertEquals("line 1:0 no viable alternative at input '*'\n", this.stderrDuringParse);
1456		assertEquals("<unexpected: [@0,0:0='*',<6>,1:0], resync=*>\n", found);
1457	}
1458
1459}
1460