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.analysis.DFA;
31 import org.antlr.analysis.DFAOptimizer;
32 import org.antlr.codegen.CodeGenerator;
33 import org.antlr.tool.*;
34 import org.junit.Test;
35 
36 import java.util.List;
37 
38 public class TestCharDFAConversion extends BaseTest {
39 
40 	/** Public default constructor used by TestRig */
TestCharDFAConversion()41 	public TestCharDFAConversion() {
42 	}
43 
44 	// R A N G E S  &  S E T S
45 
testSimpleRangeVersusChar()46 	@Test public void testSimpleRangeVersusChar() throws Exception {
47 		Grammar g = new Grammar(
48 			"lexer grammar t;\n"+
49 			"A : 'a'..'z' '@' | 'k' '$' ;");
50 		g.createLookaheadDFAs();
51 		String expecting =
52 			".s0-'k'->.s1\n" +
53 			".s0-{'a'..'j', 'l'..'z'}->:s2=>1\n" +
54 			".s1-'$'->:s3=>2\n" +
55 			".s1-'@'->:s2=>1\n";
56 		checkDecision(g, 1, expecting, null);
57 	}
58 
testRangeWithDisjointSet()59 	@Test public void testRangeWithDisjointSet() throws Exception {
60 		Grammar g = new Grammar(
61 			"lexer grammar t;\n"+
62 			"A : 'a'..'z' '@'\n" +
63 			"  | ('k'|'9'|'p') '$'\n" +
64 			"  ;\n");
65 		g.createLookaheadDFAs();
66 		// must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'}
67 		String expecting =
68 			".s0-'9'->:s3=>2\n" +
69 			".s0-{'a'..'j', 'l'..'o', 'q'..'z'}->:s2=>1\n" +
70 			".s0-{'k', 'p'}->.s1\n" +
71 			".s1-'$'->:s3=>2\n" +
72 			".s1-'@'->:s2=>1\n";
73 		checkDecision(g, 1, expecting, null);
74 	}
75 
testDisjointSetCollidingWithTwoRanges()76 	@Test public void testDisjointSetCollidingWithTwoRanges() throws Exception {
77 		Grammar g = new Grammar(
78 			"lexer grammar t;\n"+
79 			"A : ('a'..'z'|'0'..'9') '@'\n" +
80 			"  | ('k'|'9'|'p') '$'\n" +
81 			"  ;\n");
82 		g.createLookaheadDFAs(false);
83 		// must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'} and 0..9
84 		// into 0..8
85 		String expecting =
86 			".s0-{'0'..'8', 'a'..'j', 'l'..'o', 'q'..'z'}->:s2=>1\n" +
87 			".s0-{'9', 'k', 'p'}->.s1\n" +
88 			".s1-'$'->:s3=>2\n" +
89 			".s1-'@'->:s2=>1\n";
90 		checkDecision(g, 1, expecting, null);
91 	}
92 
testDisjointSetCollidingWithTwoRangesCharsFirst()93 	@Test public void testDisjointSetCollidingWithTwoRangesCharsFirst() throws Exception {
94 		Grammar g = new Grammar(
95 			"lexer grammar t;\n"+
96 			"A : ('k'|'9'|'p') '$'\n" +
97 			"  | ('a'..'z'|'0'..'9') '@'\n" +
98 			"  ;\n");
99 		// must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'} and 0..9
100 		// into 0..8
101 		String expecting =
102 			".s0-{'0'..'8', 'a'..'j', 'l'..'o', 'q'..'z'}->:s3=>2\n" +
103 			".s0-{'9', 'k', 'p'}->.s1\n" +
104 			".s1-'$'->:s2=>1\n" +
105 			".s1-'@'->:s3=>2\n";
106 		checkDecision(g, 1, expecting, null);
107 	}
108 
testDisjointSetCollidingWithTwoRangesAsSeparateAlts()109 	@Test public void testDisjointSetCollidingWithTwoRangesAsSeparateAlts() throws Exception {
110 		Grammar g = new Grammar(
111 			"lexer grammar t;\n"+
112 			"A : 'a'..'z' '@'\n" +
113 			"  | 'k' '$'\n" +
114 			"  | '9' '$'\n" +
115 			"  | 'p' '$'\n" +
116 			"  | '0'..'9' '@'\n" +
117 			"  ;\n");
118 		// must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'} and 0..9
119 		// into 0..8
120 		String expecting =
121 			".s0-'0'..'8'->:s8=>5\n" +
122 			".s0-'9'->.s6\n" +
123 			".s0-'k'->.s1\n" +
124 			".s0-'p'->.s4\n" +
125 			".s0-{'a'..'j', 'l'..'o', 'q'..'z'}->:s2=>1\n" +
126 			".s1-'$'->:s3=>2\n" +
127 			".s1-'@'->:s2=>1\n" +
128 			".s4-'$'->:s5=>4\n" +
129 			".s4-'@'->:s2=>1\n" +
130 			".s6-'$'->:s7=>3\n" +
131 			".s6-'@'->:s8=>5\n";
132 		checkDecision(g, 1, expecting, null);
133 	}
134 
testKeywordVersusID()135 	@Test public void testKeywordVersusID() throws Exception {
136 		Grammar g = new Grammar(
137 			"lexer grammar t;\n"+
138 			"IF : 'if' ;\n" + // choose this over ID
139 			"ID : ('a'..'z')+ ;\n");
140 		String expecting =
141 			".s0-'a'..'z'->:s2=>1\n" +
142 			".s0-<EOT>->:s1=>2\n";
143 		checkDecision(g, 1, expecting, null);
144 		expecting =
145 			".s0-'i'->.s1\n" +
146 			".s0-{'a'..'h', 'j'..'z'}->:s4=>2\n" +
147 			".s1-'f'->.s2\n" +
148 			".s1-<EOT>->:s4=>2\n" +
149 			".s2-'a'..'z'->:s4=>2\n" +
150 			".s2-<EOT>->:s3=>1\n";
151 		checkDecision(g, 2, expecting, null);
152 	}
153 
testIdenticalRules()154 	@Test public void testIdenticalRules() throws Exception {
155 		Grammar g = new Grammar(
156 			"lexer grammar t;\n"+
157 			"A : 'a' ;\n" +
158 			"B : 'a' ;\n"); // can't reach this
159 		String expecting =
160 			".s0-'a'->.s1\n" +
161 			".s1-<EOT>->:s2=>1\n";
162 
163 		ErrorQueue equeue = new ErrorQueue();
164 		ErrorManager.setErrorListener(equeue);
165 
166 		checkDecision(g, 1, expecting, new int[] {2});
167 
168 		assertEquals("unexpected number of expected problems",
169 				    1, equeue.size());
170 		Message msg = (Message)equeue.errors.get(0);
171 		assertTrue("warning must be an unreachable alt",
172 				    msg instanceof GrammarUnreachableAltsMessage);
173 		GrammarUnreachableAltsMessage u = (GrammarUnreachableAltsMessage)msg;
174 		assertEquals("[2]", u.alts.toString());
175 
176 	}
177 
testAdjacentNotCharLoops()178 	@Test public void testAdjacentNotCharLoops() throws Exception {
179 		Grammar g = new Grammar(
180 			"lexer grammar t;\n"+
181 			"A : (~'r')+ ;\n" +
182 			"B : (~'s')+ ;\n");
183 		String expecting =
184 			".s0-'r'->:s3=>2\n" +
185 			".s0-'s'->:s2=>1\n" +
186 			".s0-{'\\u0000'..'q', 't'..'\\uFFFF'}->.s1\n" +
187 			".s1-'r'->:s3=>2\n" +
188 			".s1-<EOT>->:s2=>1\n" +
189 			".s1-{'\\u0000'..'q', 't'..'\\uFFFF'}->.s1\n";
190 		checkDecision(g, 3, expecting, null);
191 	}
192 
testNonAdjacentNotCharLoops()193 	@Test public void testNonAdjacentNotCharLoops() throws Exception {
194 		Grammar g = new Grammar(
195 			"lexer grammar t;\n"+
196 			"A : (~'r')+ ;\n" +
197 			"B : (~'t')+ ;\n");
198 		String expecting =
199 			".s0-'r'->:s3=>2\n" +
200 			".s0-'t'->:s2=>1\n" +
201 			".s0-{'\\u0000'..'q', 's', 'u'..'\\uFFFF'}->.s1\n" +
202 			".s1-'r'->:s3=>2\n" +
203 			".s1-<EOT>->:s2=>1\n" +
204 			".s1-{'\\u0000'..'q', 's', 'u'..'\\uFFFF'}->.s1\n";
205 		checkDecision(g, 3, expecting, null);
206 	}
207 
testLoopsWithOptimizedOutExitBranches()208 	@Test public void testLoopsWithOptimizedOutExitBranches() throws Exception {
209 		Grammar g = new Grammar(
210 			"lexer grammar t;\n"+
211 			"A : 'x'* ~'x'+ ;\n");
212 		String expecting =
213 			".s0-'x'->:s1=>1\n" +
214 			".s0-{'\\u0000'..'w', 'y'..'\\uFFFF'}->:s2=>2\n";
215 		checkDecision(g, 1, expecting, null);
216 
217 		// The optimizer yanks out all exit branches from EBNF blocks
218 		// This is ok because we've already verified there are no problems
219 		// with the enter/exit decision
220 		DFAOptimizer optimizer = new DFAOptimizer(g);
221 		optimizer.optimize();
222 		FASerializer serializer = new FASerializer(g);
223 		DFA dfa = g.getLookaheadDFA(1);
224 		String result = serializer.serialize(dfa.startState);
225 		expecting = ".s0-'x'->:s1=>1\n";
226 		assertEquals(expecting, result);
227 	}
228 
229 	// N O N G R E E D Y
230 
testNonGreedy()231 	@Test public void testNonGreedy() throws Exception {
232 		Grammar g = new Grammar(
233 			"lexer grammar t;\n"+
234 			"CMT : '/*' ( options {greedy=false;} : . )* '*/' ;");
235 		String expecting =
236 			".s0-'*'->.s1\n" +
237 			".s0-{'\\u0000'..')', '+'..'\\uFFFF'}->:s3=>1\n" +
238 			".s1-'/'->:s2=>2\n" +
239 			".s1-{'\\u0000'..'.', '0'..'\\uFFFF'}->:s3=>1\n";
240 		checkDecision(g, 1, expecting, null);
241 	}
242 
testNonGreedyWildcardStar()243 	@Test public void testNonGreedyWildcardStar() throws Exception {
244 		Grammar g = new Grammar(
245 			"lexer grammar t;\n"+
246 			"SLCMT : '//' ( options {greedy=false;} : . )* '\n' ;");
247 		String expecting =
248 			".s0-'\\n'->:s1=>2\n" +
249 			".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
250 		checkDecision(g, 1, expecting, null);
251 	}
252 
testNonGreedyByDefaultWildcardStar()253 	@Test public void testNonGreedyByDefaultWildcardStar() throws Exception {
254 		Grammar g = new Grammar(
255 			"lexer grammar t;\n"+
256 			"SLCMT : '//' .* '\n' ;");
257 		String expecting =
258 			".s0-'\\n'->:s1=>2\n" +
259 			".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
260 		checkDecision(g, 1, expecting, null);
261 	}
262 
testNonGreedyWildcardPlus()263 	@Test public void testNonGreedyWildcardPlus() throws Exception {
264 		// same DFA as nongreedy .* but code gen checks number of
265 		// iterations at runtime
266 		Grammar g = new Grammar(
267 			"lexer grammar t;\n"+
268 			"SLCMT : '//' ( options {greedy=false;} : . )+ '\n' ;");
269 		String expecting =
270 			".s0-'\\n'->:s1=>2\n" +
271 			".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
272 		checkDecision(g, 1, expecting, null);
273 	}
274 
testNonGreedyByDefaultWildcardPlus()275 	@Test public void testNonGreedyByDefaultWildcardPlus() throws Exception {
276 		Grammar g = new Grammar(
277 			"lexer grammar t;\n"+
278 			"SLCMT : '//' .+ '\n' ;");
279 		String expecting =
280 			".s0-'\\n'->:s1=>2\n" +
281 			".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
282 		checkDecision(g, 1, expecting, null);
283 	}
284 
testNonGreedyByDefaultWildcardPlusWithParens()285 	@Test public void testNonGreedyByDefaultWildcardPlusWithParens() throws Exception {
286 		Grammar g = new Grammar(
287 			"lexer grammar t;\n"+
288 			"SLCMT : '//' (.)+ '\n' ;");
289 		String expecting =
290 			".s0-'\\n'->:s1=>2\n" +
291 			".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
292 		checkDecision(g, 1, expecting, null);
293 	}
294 
testNonWildcardNonGreedy()295 	@Test public void testNonWildcardNonGreedy() throws Exception {
296 		Grammar g = new Grammar(
297 			"lexer grammar t;\n"+
298 			"DUH : (options {greedy=false;}:'x'|'y')* 'xy' ;");
299 		String expecting =
300 			".s0-'x'->.s1\n" +
301 			".s0-'y'->:s4=>2\n" +
302 			".s1-'x'->:s3=>1\n" +
303 			".s1-'y'->:s2=>3\n";
304 		checkDecision(g, 1, expecting, null);
305 	}
306 
testNonWildcardEOTMakesItWorkWithoutNonGreedyOption()307 	@Test public void testNonWildcardEOTMakesItWorkWithoutNonGreedyOption() throws Exception {
308 		Grammar g = new Grammar(
309 			"lexer grammar t;\n"+
310 			"DUH : ('x'|'y')* 'xy' ;");
311 		String expecting =
312 			".s0-'x'->.s1\n" +
313 			".s0-'y'->:s4=>1\n" +
314 			".s1-'x'->:s4=>1\n" +
315 			".s1-'y'->.s2\n" +
316 			".s2-'x'..'y'->:s4=>1\n" +
317 			".s2-<EOT>->:s3=>2\n";
318 		checkDecision(g, 1, expecting, null);
319 	}
320 
testAltConflictsWithLoopThenExit()321 	@Test public void testAltConflictsWithLoopThenExit() throws Exception {
322 		// \" predicts alt 1, but wildcard then " can predict exit also
323 		Grammar g = new Grammar(
324 			"lexer grammar t;\n"+
325 			"STRING : '\"' (options {greedy=false;}: '\\\\\"' | .)* '\"' ;\n"
326 		);
327 		String expecting =
328 			".s0-'\"'->:s1=>3\n" +
329 				".s0-'\\\\'->.s2\n" +
330 				".s0-{'\\u0000'..'!', '#'..'[', ']'..'\\uFFFF'}->:s4=>2\n" +
331 				".s2-'\"'->:s3=>1\n" +
332 				".s2-{'\\u0000'..'!', '#'..'\\uFFFF'}->:s4=>2\n";
333 		checkDecision(g, 1, expecting, null);
334 	}
335 
testNonGreedyLoopThatNeverLoops()336 	@Test public void testNonGreedyLoopThatNeverLoops() throws Exception {
337 		Grammar g = new Grammar(
338 			"lexer grammar t;\n"+
339 			"DUH : (options {greedy=false;}:'x')+ ;"); // loop never matched
340 		String expecting =
341 			":s0=>2\n";
342 
343 		ErrorQueue equeue = new ErrorQueue();
344 		ErrorManager.setErrorListener(equeue);
345 
346 		checkDecision(g, 1, expecting, new int[] {1});
347 
348 		assertEquals("unexpected number of expected problems",
349 				    1, equeue.size());
350 		Message msg = (Message)equeue.errors.get(0);
351 		assertTrue("warning must be an unreachable alt",
352 				   msg instanceof GrammarUnreachableAltsMessage);
353 		GrammarUnreachableAltsMessage u = (GrammarUnreachableAltsMessage)msg;
354 		assertEquals("[1]", u.alts.toString());
355 	}
356 
testRecursive()357 	@Test public void testRecursive() throws Exception {
358 		// this is cool because the 3rd alt includes !(all other possibilities)
359 		Grammar g = new Grammar(
360 			"lexer grammar duh;\n" +
361 			"SUBTEMPLATE\n" +
362 			"        :       '{'\n" +
363 			"                ( SUBTEMPLATE\n" +
364 			"                | ESC\n" +
365 			"                | ~('}'|'\\\\'|'{')\n" +
366 			"                )*\n" +
367 			"                '}'\n" +
368 			"        ;\n" +
369 			"fragment\n" +
370 			"ESC     :       '\\\\' . ;");
371 		g.createLookaheadDFAs();
372 		String expecting =
373 			".s0-'\\\\'->:s2=>2\n" +
374 			".s0-'{'->:s1=>1\n" +
375 			".s0-'}'->:s4=>4\n" +
376 			".s0-{'\\u0000'..'[', ']'..'z', '|', '~'..'\\uFFFF'}->:s3=>3\n";
377 		checkDecision(g, 1, expecting, null);
378 	}
379 
testRecursive2()380 	@Test public void testRecursive2() throws Exception {
381 		// this is also cool because it resolves \\ to be ESC alt; it's just
382 		// less efficient of a DFA
383 		Grammar g = new Grammar(
384 			"lexer grammar duh;\n" +
385 			"SUBTEMPLATE\n" +
386 			"        :       '{'\n" +
387 			"                ( SUBTEMPLATE\n" +
388 			"                | ESC\n" +
389 			"                | ~('}'|'{')\n" +
390 			"                )*\n" +
391 			"                '}'\n" +
392 			"        ;\n" +
393 			"fragment\n" +
394 			"ESC     :       '\\\\' . ;");
395 		g.createLookaheadDFAs();
396 		String expecting =
397 			".s0-'\\\\'->.s3\n" +
398 			".s0-'{'->:s2=>1\n" +
399 			".s0-'}'->:s1=>4\n" +
400 			".s0-{'\\u0000'..'[', ']'..'z', '|', '~'..'\\uFFFF'}->:s5=>3\n" +
401 			".s3-'\\\\'->:s8=>2\n" +
402 			".s3-'{'->:s7=>2\n" +
403 			".s3-'}'->.s4\n" +
404 			".s3-{'\\u0000'..'[', ']'..'z', '|', '~'..'\\uFFFF'}->:s6=>2\n" +
405 			".s4-'\\u0000'..'\\uFFFF'->:s6=>2\n" +
406 			".s4-<EOT>->:s5=>3\n";
407 		checkDecision(g, 1, expecting, null);
408 	}
409 
testNotFragmentInLexer()410 	@Test public void testNotFragmentInLexer() throws Exception {
411 		Grammar g = new Grammar(
412 			"lexer grammar T;\n"+
413 			"A : 'a' | ~B {;} ;\n" +
414 			"fragment B : 'a' ;\n");
415 		g.createLookaheadDFAs();
416 		String expecting =
417 			".s0-'a'->:s1=>1\n" +
418 			".s0-{'\\u0000'..'`', 'b'..'\\uFFFF'}->:s2=>2\n";
419 		checkDecision(g, 1, expecting, null);
420 	}
421 
testNotSetFragmentInLexer()422 	@Test public void testNotSetFragmentInLexer() throws Exception {
423 		Grammar g = new Grammar(
424 			"lexer grammar T;\n"+
425 			"A : B | ~B {;} ;\n" +
426 			"fragment B : 'a'|'b' ;\n");
427 		g.createLookaheadDFAs();
428 		String expecting =
429 			".s0-'a'..'b'->:s1=>1\n" +
430 			".s0-{'\\u0000'..'`', 'c'..'\\uFFFF'}->:s2=>2\n";
431 		checkDecision(g, 1, expecting, null);
432 	}
433 
testNotTokenInLexer()434 	@Test public void testNotTokenInLexer() throws Exception {
435 		Grammar g = new Grammar(
436 			"lexer grammar T;\n"+
437 			"A : 'x' ('a' | ~B {;}) ;\n" +
438 			"B : 'a' ;\n");
439 		g.createLookaheadDFAs();
440 		String expecting =
441 			".s0-'a'->:s1=>1\n" +
442 			".s0-{'\\u0000'..'`', 'b'..'\\uFFFF'}->:s2=>2\n";
443 		checkDecision(g, 1, expecting, null);
444 	}
445 
testNotComplicatedSetRuleInLexer()446 	@Test public void testNotComplicatedSetRuleInLexer() throws Exception {
447 		Grammar g = new Grammar(
448 			"lexer grammar T;\n"+
449 			"A : B | ~B {;} ;\n" +
450 			"fragment B : 'a'|'b'|'c'..'e'|C ;\n" +
451 			"fragment C : 'f' ;\n"); // has to seen from B to C
452 		String expecting =
453 			".s0-'a'..'f'->:s1=>1\n" +
454 			".s0-{'\\u0000'..'`', 'g'..'\\uFFFF'}->:s2=>2\n";
455 		checkDecision(g, 1, expecting, null);
456 	}
457 
testNotSetWithRuleInLexer()458 	@Test public void testNotSetWithRuleInLexer() throws Exception {
459 		Grammar g = new Grammar(
460 			"lexer grammar T;\n"+
461 			"T : ~('a' | B) | 'a';\n" +
462 			"fragment\n" +
463 			"B : 'b' ;\n" +
464 			"C : ~'x'{;} ;"); // force Tokens to not collapse T|C
465 		String expecting =
466 			".s0-'b'->:s3=>2\n" +
467 			".s0-'x'->:s2=>1\n" +
468 			".s0-{'\\u0000'..'a', 'c'..'w', 'y'..'\\uFFFF'}->.s1\n" +
469 			".s1-<EOT>->:s2=>1\n";
470 		checkDecision(g, 1, expecting, null);
471 	}
472 
testSetCallsRuleWithNot()473 	@Test public void testSetCallsRuleWithNot() throws Exception {
474 		Grammar g = new Grammar(
475 			"lexer grammar A;\n" +
476 			"T : ~'x' ;\n" +
477 			"S : 'x' (T | 'x') ;\n");
478 		String expecting =
479 			".s0-'x'->:s2=>2\n" +
480 			".s0-{'\\u0000'..'w', 'y'..'\\uFFFF'}->:s1=>1\n";
481 		checkDecision(g, 1, expecting, null);
482 	}
483 
testSynPredInLexer()484 	@Test public void testSynPredInLexer() throws Exception {
485 		Grammar g = new Grammar(
486 			"lexer grammar T;\n"+
487 			"LT:  '<' ' '*\n" +
488 			"  |  ('<' IDENT) => '<' IDENT '>'\n" + // this was causing syntax error
489 			"  ;\n" +
490 			"IDENT:    'a'+;\n");
491 		// basically, Tokens rule should not do set compression test
492 		String expecting =
493 			".s0-'<'->:s1=>1\n" +
494 			".s0-'a'->:s2=>2\n";
495 		checkDecision(g, 4, expecting, null); // 4 is Tokens rule
496 	}
497 
498 	// S U P P O R T
499 
_template()500 	public void _template() throws Exception {
501 		Grammar g = new Grammar(
502 			"grammar T;\n"+
503 			"a : A | B;");
504 		String expecting =
505 			"\n";
506 		checkDecision(g, 1, expecting, null);
507 	}
508 
checkDecision(Grammar g, int decision, String expecting, int[] expectingUnreachableAlts)509 	protected void checkDecision(Grammar g,
510 								 int decision,
511 								 String expecting,
512 								 int[] expectingUnreachableAlts)
513 		throws Exception
514 	{
515 
516 		// mimic actions of org.antlr.Tool first time for grammar g
517 		if ( g.getCodeGenerator()==null ) {
518 			CodeGenerator generator = new CodeGenerator(null, g, "Java");
519 			g.setCodeGenerator(generator);
520 			g.buildNFA();
521 			g.createLookaheadDFAs(false);
522 		}
523 
524 		DFA dfa = g.getLookaheadDFA(decision);
525 		assertNotNull("unknown decision #"+decision, dfa);
526 		FASerializer serializer = new FASerializer(g);
527 		String result = serializer.serialize(dfa.startState);
528 		//System.out.print(result);
529 		List nonDetAlts = dfa.getUnreachableAlts();
530 		//System.out.println("alts w/o predict state="+nonDetAlts);
531 
532 		// first make sure nondeterministic alts are as expected
533 		if ( expectingUnreachableAlts==null ) {
534 			if ( nonDetAlts!=null && nonDetAlts.size()!=0 ) {
535 				System.err.println("nondeterministic alts (should be empty): "+nonDetAlts);
536 			}
537 			assertEquals("unreachable alts mismatch", 0, nonDetAlts!=null?nonDetAlts.size():0);
538 		}
539 		else {
540 			for (int i=0; i<expectingUnreachableAlts.length; i++) {
541 				assertTrue("unreachable alts mismatch",
542 						   nonDetAlts!=null?nonDetAlts.contains(new Integer(expectingUnreachableAlts[i])):false);
543 			}
544 		}
545 		assertEquals(expecting, result);
546 	}
547 
548 }
549