Home | History | Annotate | Download | only in test
      1 /*
      2  * [The "BSD license"]
      3  *  Copyright (c) 2010 Terence Parr
      4  *  All rights reserved.
      5  *
      6  *  Redistribution and use in source and binary forms, with or without
      7  *  modification, are permitted provided that the following conditions
      8  *  are met:
      9  *  1. Redistributions of source code must retain the above copyright
     10  *      notice, this list of conditions and the following disclaimer.
     11  *  2. Redistributions in binary form must reproduce the above copyright
     12  *      notice, this list of conditions and the following disclaimer in the
     13  *      documentation and/or other materials provided with the distribution.
     14  *  3. The name of the author may not be used to endorse or promote products
     15  *      derived from this software without specific prior written permission.
     16  *
     17  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 package org.antlr.test;
     29 
     30 import org.antlr.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 */
     41 	public TestCharDFAConversion() {
     42 	}
     43 
     44 	// R A N G E S  &  S E T S
     45 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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