001: /*
002: [The "BSD licence"]
003: Copyright (c) 2005-2006 Terence Parr
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009: 1. Redistributions of source code must retain the above copyright
010: notice, this list of conditions and the following disclaimer.
011: 2. Redistributions in binary form must reproduce the above copyright
012: notice, this list of conditions and the following disclaimer in the
013: documentation and/or other materials provided with the distribution.
014: 3. The name of the author may not be used to endorse or promote products
015: derived from this software without specific prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
018: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
019: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
020: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
021: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028: package org.antlr.test;
029:
030: import org.antlr.analysis.DFA;
031: import org.antlr.analysis.DecisionProbe;
032: import org.antlr.codegen.CodeGenerator;
033: import org.antlr.misc.BitSet;
034: import org.antlr.tool.*;
035:
036: import java.util.List;
037:
038: public class TestSemanticPredicates extends BaseTest {
039:
040: /** Public default constructor used by TestRig */
041: public TestSemanticPredicates() {
042: }
043:
044: public void testPredsButSyntaxResolves() throws Exception {
045: Grammar g = new Grammar("parser grammar P;\n"
046: + "a : {p1}? A | {p2}? B ;");
047: String expecting = ".s0-A->:s1=>1\n" + ".s0-B->:s2=>2\n";
048: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
049: }
050:
051: public void testLL_1_Pred() throws Exception {
052: Grammar g = new Grammar("parser grammar P;\n"
053: + "a : {p1}? A | {p2}? A ;");
054: String expecting = ".s0-A->.s1\n" + ".s1-{p1}?->:s2=>1\n"
055: + ".s1-{p2}?->:s3=>2\n";
056: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
057: }
058:
059: public void testLL_2_Pred() throws Exception {
060: Grammar g = new Grammar("parser grammar P;\n"
061: + "a : {p1}? A B | {p2}? A B ;");
062: String expecting = ".s0-A->.s1\n" + ".s1-B->.s2\n"
063: + ".s2-{p1}?->:s3=>1\n" + ".s2-{p2}?->:s4=>2\n";
064: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
065: }
066:
067: public void testPredicatedLoop() throws Exception {
068: Grammar g = new Grammar("parser grammar P;\n"
069: + "a : ( {p1}? A | {p2}? A )+;");
070: String expecting = // loop back
071: ".s0-A->.s2\n" + ".s0-EOF->:s1=>3\n" + ".s2-{p1}?->:s3=>1\n"
072: + ".s2-{p2}?->:s4=>2\n";
073: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
074: }
075:
076: public void testPredicatedToStayInLoop() throws Exception {
077: Grammar g = new Grammar("parser grammar P;\n"
078: + "a : ( {p1}? A )+ (A)+;");
079: String expecting = ".s0-A->.s1\n" + ".s1-{!(p1)}?->:s2=>1\n"
080: + ".s1-{p1}?->:s3=>2\n"; // loop back
081: }
082:
083: public void testAndPredicates() throws Exception {
084: Grammar g = new Grammar("parser grammar P;\n"
085: + "a : {p1}? {p1a}? A | {p2}? A ;");
086: String expecting = ".s0-A->.s1\n"
087: + ".s1-{(p1&&p1a)}?->:s2=>1\n" + ".s1-{p2}?->:s3=>2\n";
088: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
089: }
090:
091: public void testOrPredicates() throws Exception {
092: Grammar g = new Grammar("parser grammar P;\n"
093: + "a : b | {p2}? A ;\n" + "b : {p1}? A | {p1a}? A ;");
094: String expecting = ".s0-A->.s1\n"
095: + ".s1-{(p1||p1a)}?->:s2=>1\n" + ".s1-{p2}?->:s3=>2\n";
096: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
097: }
098:
099: public void testIgnoresHoistingDepthGreaterThanZero()
100: throws Exception {
101: Grammar g = new Grammar("parser grammar P;\n"
102: + "a : A {p1}? | A {p2}?;");
103: String expecting = ".s0-A->:s1=>1\n";
104: checkDecision(g, 1, expecting, new int[] { 2 }, new int[] { 1,
105: 2 }, "A", null, null, 2);
106: }
107:
108: public void testHoist2() throws Exception {
109: Grammar g = new Grammar("parser grammar P;\n" + "a : b | c ;\n"
110: + "b : {p1}? A ;\n" + "c : {p2}? A ;\n");
111: String expecting = ".s0-A->.s1\n" + ".s1-{p1}?->:s2=>1\n"
112: + ".s1-{p2}?->:s3=>2\n";
113: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
114: }
115:
116: public void testHoistCorrectContext() throws Exception {
117: Grammar g = new Grammar("parser grammar P;\n"
118: + "a : b | {p2}? ID ;\n" + "b : {p1}? ID | INT ;\n");
119: String expecting = // only tests after ID, not INT :)
120: ".s0-ID->.s1\n" + ".s0-INT->:s2=>1\n" + ".s1-{p1}?->:s2=>1\n"
121: + ".s1-{p2}?->:s3=>2\n";
122: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
123: }
124:
125: public void testDefaultPredNakedAltIsLast() throws Exception {
126: Grammar g = new Grammar("parser grammar P;\n"
127: + "a : b | ID ;\n" + "b : {p1}? ID | INT ;\n");
128: String expecting = ".s0-ID->.s1\n" + ".s0-INT->:s2=>1\n"
129: + ".s1-{p1}?->:s2=>1\n" + ".s1-{true}?->:s3=>2\n";
130: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
131: }
132:
133: public void testDefaultPredNakedAltNotLast() throws Exception {
134: Grammar g = new Grammar("parser grammar P;\n"
135: + "a : ID | b ;\n" + "b : {p1}? ID | INT ;\n");
136: String expecting = ".s0-ID->.s1\n" + ".s0-INT->:s3=>2\n"
137: + ".s1-{!(p1)}?->:s2=>1\n" + ".s1-{p1}?->:s3=>2\n";
138: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
139: }
140:
141: public void testLeftRecursivePred() throws Exception {
142: Grammar g = new Grammar("parser grammar P;\n" + "s : a ;\n"
143: + "a : {p1}? a | ID ;\n");
144: String expecting = ".s0-ID->.s1\n" + ".s1-{p1}?->:s2=>1\n"
145: + ".s1-{true}?->:s3=>2\n";
146:
147: DecisionProbe.verbose = true; // make sure we get all error info
148: ErrorQueue equeue = new ErrorQueue();
149: ErrorManager.setErrorListener(equeue);
150: CodeGenerator generator = new CodeGenerator(newTool(), g,
151: "Java");
152: g.setCodeGenerator(generator);
153: if (g.getNumberOfDecisions() == 0) {
154: g.createNFAs();
155: g.createLookaheadDFAs();
156: }
157:
158: DFA dfa = g.getLookaheadDFA(1);
159: FASerializer serializer = new FASerializer(g);
160: String result = serializer.serialize(dfa.startState);
161: assertEquals(expecting, result);
162:
163: assertEquals("unexpected number of expected problems", 1,
164: equeue.size());
165: Message msg = (Message) equeue.warnings.get(0);
166: assertTrue("warning must be a recursion overflow msg",
167: msg instanceof RecursionOverflowMessage);
168: }
169:
170: public void testIgnorePredFromLL2AltLastAltIsDefaultTrue()
171: throws Exception {
172: Grammar g = new Grammar("parser grammar P;\n"
173: + "a : {p1}? A B | A C | {p2}? A | {p3}? A | A ;\n");
174: // two situations of note:
175: // 1. A B syntax is enough to predict that alt, so p1 is not used
176: // to distinguish it from alts 2..5
177: // 2. Alts 3, 4, 5 are nondeterministic with upon A. p2, p3 and the
178: // complement of p2||p3 is sufficient to resolve the conflict. Do
179: // not include alt 1's p1 pred in the "complement of other alts"
180: // because it is not considered nondeterministic with alts 3..5
181: String expecting = ".s0-A->.s1\n" + ".s1-B->:s2=>1\n"
182: + ".s1-C->:s3=>2\n" + ".s1-{p2}?->:s4=>3\n"
183: + ".s1-{p3}?->:s5=>4\n" + ".s1-{true}?->:s6=>5\n";
184: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
185: }
186:
187: public void testIgnorePredFromLL2AltPredUnionNeeded()
188: throws Exception {
189: Grammar g = new Grammar("parser grammar P;\n"
190: + "a : {p1}? A B | A C | {p2}? A | A | {p3}? A ;\n");
191: // two situations of note:
192: // 1. A B syntax is enough to predict that alt, so p1 is not used
193: // to distinguish it from alts 2..5
194: // 2. Alts 3, 4, 5 are nondeterministic with upon A. p2, p3 and the
195: // complement of p2||p3 is sufficient to resolve the conflict. Do
196: // not include alt 1's p1 pred in the "complement of other alts"
197: // because it is not considered nondeterministic with alts 3..5
198: String expecting = ".s0-A->.s1\n" + ".s1-B->:s2=>1\n"
199: + ".s1-C->:s3=>2\n" + ".s1-{!((p3||p2))}?->:s5=>4\n"
200: + ".s1-{p2}?->:s4=>3\n" + ".s1-{p3}?->:s6=>5\n";
201: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
202: }
203:
204: public void testPredGets2SymbolSyntacticContext() throws Exception {
205: Grammar g = new Grammar("parser grammar P;\n"
206: + "a : b | A B | C ;\n" + "b : {p1}? A B ;\n");
207: String expecting = ".s0-A->.s1\n" + ".s0-C->:s5=>3\n"
208: + ".s1-B->.s2\n" + ".s2-{p1}?->:s3=>1\n"
209: + ".s2-{true}?->:s4=>2\n";
210: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
211: }
212:
213: public void testMatchesLongestThenTestPred() throws Exception {
214: Grammar g = new Grammar("parser grammar P;\n" + "a : b | c ;\n"
215: + "b : {p}? A ;\n" + "c : {q}? (A|B)+ ;");
216: String expecting = ".s0-A->.s1\n" + ".s0-B->:s3=>2\n"
217: + ".s1-{p}?->:s2=>1\n" + ".s1-{q}?->:s3=>2\n";
218: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
219: }
220:
221: public void testPredsUsedAfterRecursionOverflow() throws Exception {
222: Grammar g = new Grammar("grammar P;\n"
223: + "s : {p1}? e '.' | {p2}? e ':' ;\n"
224: + "e : '(' e ')' | INT ;\n");
225: String expecting = ".s0-'('->.s1\n" + ".s0-INT->.s7\n"
226: + ".s1-'('->.s2\n" + ".s1-INT->.s5\n"
227: + ".s2-{p1}?->:s3=>1\n" + ".s2-{p2}?->:s4=>2\n"
228: + ".s5-')'->.s6\n" + ".s6-'.'->:s3=>1\n"
229: + ".s6-':'->:s4=>2\n" + ".s7-'.'->:s3=>1\n"
230: + ".s7-':'->:s4=>2\n";
231: DecisionProbe.verbose = true; // make sure we get all error info
232: ErrorQueue equeue = new ErrorQueue();
233: ErrorManager.setErrorListener(equeue);
234: CodeGenerator generator = new CodeGenerator(newTool(), g,
235: "Java");
236: g.setCodeGenerator(generator);
237: if (g.getNumberOfDecisions() == 0) {
238: g.createNFAs();
239: g.createLookaheadDFAs();
240: }
241:
242: assertEquals("unexpected number of expected problems", 0,
243: equeue.size());
244: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
245: }
246:
247: public void testLexerMatchesLongestThenTestPred() throws Exception {
248: Grammar g = new Grammar("lexer grammar P;\n"
249: + "B : {p}? 'a' ;\n" + "C : {q}? ('a'|'b')+ ;");
250: String expecting = ".s0-'a'->.s1\n" + ".s0-'b'->:s4=>2\n"
251: + ".s1-'a'..'b'->:s4=>2\n" + ".s1-<EOT>->.s2\n"
252: + ".s2-{p}?->:s3=>1\n" + ".s2-{q}?->:s4=>2\n";
253: checkDecision(g, 2, expecting, null, null, null, null, null, 0);
254: }
255:
256: public void testGatedPred() throws Exception {
257: // gated preds are present on all arcs in predictor
258: Grammar g = new Grammar("lexer grammar P;\n"
259: + "B : {p}? => 'a' ;\n" + "C : {q}? => ('a'|'b')+ ;");
260: String expecting = ".s0-'a'&&{(p||q)}?->.s1\n"
261: + ".s0-'b'&&{q}?->:s4=>2\n"
262: + ".s1-'a'..'b'&&{q}?->:s4=>2\n"
263: + ".s1-<EOT>&&{(p||q)}?->.s2\n" + ".s2-{p}?->:s3=>1\n"
264: + ".s2-{q}?->:s4=>2\n";
265: checkDecision(g, 2, expecting, null, null, null, null, null, 0);
266: }
267:
268: public void testGatedPredHoistsAndCanBeInStopState()
269: throws Exception {
270: // I found a bug where merging stop states made us throw away
271: // a stop state with a gated pred!
272: Grammar g = new Grammar("grammar u;\n" + "a : b+ ;\n"
273: + "b : 'x' | {p}?=> 'y' ;");
274: String expecting = ".s0-'x'->:s2=>1\n"
275: + ".s0-'y'&&{p}?->:s3=>1\n" + ".s0-EOF->:s1=>2\n";
276: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
277: }
278:
279: public void testGatedPredInCyclicDFA() throws Exception {
280: Grammar g = new Grammar("lexer grammar P;\n"
281: + "A : {p}?=> ('a')+ 'x' ;\n"
282: + "B : {q}?=> ('a'|'b')+ 'x' ;");
283: String expecting = ".s0-'a'&&{(p||q)}?->.s1\n"
284: + ".s0-'b'&&{q}?->:s5=>2\n"
285: + ".s1-'a'&&{(p||q)}?->.s1\n"
286: + ".s1-'b'&&{q}?->:s5=>2\n"
287: + ".s1-'x'&&{(p||q)}?->.s2\n"
288: + ".s2-<EOT>&&{(p||q)}?->.s3\n" + ".s3-{p}?->:s4=>1\n"
289: + ".s3-{q}?->:s5=>2\n";
290: checkDecision(g, 3, expecting, null, null, null, null, null, 0);
291: }
292:
293: public void testGatedPredNotActuallyUsedOnEdges() throws Exception {
294: Grammar g = new Grammar("lexer grammar P;\n"
295: + "A : ('a' | {p}?=> 'a')\n" + " | 'a' 'b'\n" + " ;");
296: String expecting1 = ".s0-'a'->.s1\n" + ".s1-{!(p)}?->:s2=>1\n" + // Used to disambig subrule
297: ".s1-{p}?->:s3=>2\n";
298: // rule A decision can't test p from s0->1 because 'a' is valid
299: // for alt1 *and* alt2 w/o p. Can't test p from s1 to s3 because
300: // we might have passed the first alt of subrule. The same state
301: // is listed in s2 in 2 different configurations: one with and one
302: // w/o p. Can't test therefore. p||true == true.
303: String expecting2 = ".s0-'a'->.s1\n" + ".s1-'b'->:s2=>2\n"
304: + ".s1-<EOT>->:s3=>1\n";
305: checkDecision(g, 1, expecting1, null, null, null, null, null, 0);
306: checkDecision(g, 2, expecting2, null, null, null, null, null, 0);
307: }
308:
309: public void testGatedPredDoesNotForceAllToBeGated()
310: throws Exception {
311: Grammar g = new Grammar("grammar w;\n" + "a : b | c ;\n"
312: + "b : {p}? B ;\n" + "c : {q}?=> d ;\n"
313: + "d : {r}? C ;\n");
314: String expecting = ".s0-B->:s1=>1\n" + ".s0-C&&{q}?->:s2=>2\n";
315: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
316: }
317:
318: public void testGatedPredDoesNotForceAllToBeGated2()
319: throws Exception {
320: Grammar g = new Grammar("grammar w;\n" + "a : b | c ;\n"
321: + "b : {p}? B ;\n" + "c : {q}?=> d ;\n"
322: + "d : {r}?=> C\n" + " | B\n" + " ;\n");
323: String expecting = ".s0-B->.s1\n"
324: + ".s0-C&&{(q&&r)}?->:s3=>2\n" + ".s1-{p}?->:s2=>1\n"
325: + ".s1-{q}?->:s3=>2\n";
326: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
327: }
328:
329: public void testORGatedPred() throws Exception {
330: Grammar g = new Grammar("grammar w;\n" + "a : b | c ;\n"
331: + "b : {p}? B ;\n" + "c : {q}?=> d ;\n"
332: + "d : {r}?=> C\n" + " | {s}?=> B\n" + " ;\n");
333: String expecting = ".s0-B->.s1\n"
334: + ".s0-C&&{(q&&r)}?->:s3=>2\n"
335: + ".s1-{(q&&s)}?->:s3=>2\n" + ".s1-{p}?->:s2=>1\n";
336: checkDecision(g, 1, expecting, null, null, null, null, null, 0);
337: }
338:
339: /** The following grammar should yield an error that rule 'a' has
340: * insufficient semantic info pulled from 'b'.
341: */
342: public void testIncompleteSemanticHoistedContext() throws Exception {
343: ErrorQueue equeue = new ErrorQueue();
344: ErrorManager.setErrorListener(equeue);
345: Grammar g = new Grammar("parser grammar t;\n" + "a : b | B;\n"
346: + "b : {p1}? B | B ;");
347: String expecting = ".s0-B->:s1=>1\n";
348: checkDecision(g, 1, expecting, new int[] { 2 }, new int[] { 1,
349: 2 }, "B", new int[] { 1 }, null, 3);
350: }
351:
352: /** The following grammar should yield an error that rule 'a' has
353: * insufficient semantic info pulled from 'b'. This is the same
354: * as the previous case except that the D prevents the B path from
355: * "pinching" together into a single NFA state.
356: *
357: * This test also demonstrates that just because B D could predict
358: * alt 1 in rule 'a', it is unnecessary to continue NFA->DFA
359: * conversion to include an edge for D. Alt 1 is the only possible
360: * prediction because we resolve the ambiguity by choosing alt 1.
361: */
362: public void testIncompleteSemanticHoistedContext2()
363: throws Exception {
364: ErrorQueue equeue = new ErrorQueue();
365: ErrorManager.setErrorListener(equeue);
366: Grammar g = new Grammar("parser grammar t;\n" + "a : b | B;\n"
367: + "b : {p1}? B | B D ;");
368: String expecting = ".s0-B->:s1=>1\n";
369: checkDecision(g, 1, expecting, new int[] { 2 }, new int[] { 1,
370: 2 }, "B", new int[] { 1 }, null, 3);
371: }
372:
373: public void testTooFewSemanticPredicates() throws Exception {
374: Grammar g = new Grammar("parser grammar t;\n"
375: + "a : {p1}? A | A | A ;");
376: String expecting = ".s0-A->:s1=>1\n";
377: checkDecision(g, 1, expecting, new int[] { 2, 3 }, new int[] {
378: 1, 2, 3 }, "A", null, null, 2);
379: }
380:
381: public void testPredWithK1() throws Exception {
382: Grammar g = new Grammar("\tlexer grammar TLexer;\n" + "A\n"
383: + "options {\n" + " k=1;\n" + "}\n"
384: + " : {p1}? ('x')+ '.'\n" + " | {p2}? ('x')+ '.'\n"
385: + " ;\n");
386: String expecting = ".s0-'x'->.s1\n" + ".s1-{p1}?->:s2=>1\n"
387: + ".s1-{p2}?->:s3=>2\n";
388: int[] unreachableAlts = null;
389: int[] nonDetAlts = null;
390: String ambigInput = null;
391: int[] insufficientPredAlts = null;
392: int[] danglingAlts = null;
393: int numWarnings = 0;
394: checkDecision(g, 3, expecting, unreachableAlts, nonDetAlts,
395: ambigInput, insufficientPredAlts, danglingAlts,
396: numWarnings);
397: }
398:
399: public void testPredWithArbitraryLookahead() throws Exception {
400: Grammar g = new Grammar("\tlexer grammar TLexer;\n"
401: + "A : {p1}? ('x')+ '.'\n" + " | {p2}? ('x')+ '.'\n"
402: + " ;\n");
403: String expecting = ".s0-'x'->.s1\n" + ".s1-'.'->.s2\n"
404: + ".s1-'x'->.s1\n" + ".s2-{p1}?->:s3=>1\n"
405: + ".s2-{p2}?->:s4=>2\n";
406: int[] unreachableAlts = null;
407: int[] nonDetAlts = null;
408: String ambigInput = null;
409: int[] insufficientPredAlts = null;
410: int[] danglingAlts = null;
411: int numWarnings = 0;
412: checkDecision(g, 3, expecting, unreachableAlts, nonDetAlts,
413: ambigInput, insufficientPredAlts, danglingAlts,
414: numWarnings);
415: }
416:
417: /** For a DFA state with lots of configurations that have the same
418: * predicate, don't just OR them all together as it's a waste to
419: * test a||a||b||a||a etc... ANTLR makes a unique set and THEN
420: * OR's them together.
421: */
422: public void testUniquePredicateOR() throws Exception {
423: Grammar g = new Grammar("parser grammar v;\n" + "\n"
424: + "a : {a}? b\n" + " | {b}? b\n" + " ;\n" + "\n"
425: + "b : {c}? (X)+ ;\n" + "\n" + "c : a\n" + " | b\n"
426: + " ;\n");
427: String expecting = ".s0-X->.s1\n"
428: + ".s1-{((b&&c)||(a&&c))}?->:s2=>1\n"
429: + ".s1-{c}?->:s3=>2\n";
430: int[] unreachableAlts = null;
431: int[] nonDetAlts = null;
432: String ambigInput = null;
433: int[] insufficientPredAlts = null;
434: int[] danglingAlts = null;
435: int numWarnings = 0;
436: checkDecision(g, 3, expecting, unreachableAlts, nonDetAlts,
437: ambigInput, insufficientPredAlts, danglingAlts,
438: numWarnings);
439: }
440:
441: // S U P P O R T
442:
443: public void _template() throws Exception {
444: Grammar g = new Grammar("parser grammar t;\n" + "a : A | B;");
445: String expecting = "\n";
446: int[] unreachableAlts = null;
447: int[] nonDetAlts = new int[] { 1, 2 };
448: String ambigInput = "L ID R";
449: int[] insufficientPredAlts = new int[] { 1 };
450: int[] danglingAlts = null;
451: int numWarnings = 1;
452: checkDecision(g, 1, expecting, unreachableAlts, nonDetAlts,
453: ambigInput, insufficientPredAlts, danglingAlts,
454: numWarnings);
455: }
456:
457: protected void checkDecision(Grammar g, int decision,
458: String expecting, int[] expectingUnreachableAlts,
459: int[] expectingNonDetAlts, String expectingAmbigInput,
460: int[] expectingInsufficientPredAlts,
461: int[] expectingDanglingAlts, int expectingNumWarnings)
462: throws Exception {
463: DecisionProbe.verbose = true; // make sure we get all error info
464: ErrorQueue equeue = new ErrorQueue();
465: ErrorManager.setErrorListener(equeue);
466: CodeGenerator generator = new CodeGenerator(newTool(), g,
467: "Java");
468: g.setCodeGenerator(generator);
469: // mimic actions of org.antlr.Tool first time for grammar g
470: if (g.getNumberOfDecisions() == 0) {
471: g.createNFAs();
472: g.createLookaheadDFAs();
473: }
474:
475: if (equeue.size() != expectingNumWarnings) {
476: System.err.println("Warnings issued: " + equeue);
477: }
478:
479: assertEquals("unexpected number of expected problems",
480: expectingNumWarnings, equeue.size());
481:
482: DFA dfa = g.getLookaheadDFA(decision);
483: FASerializer serializer = new FASerializer(g);
484: String result = serializer.serialize(dfa.startState);
485: //System.out.print(result);
486: List unreachableAlts = dfa.getUnreachableAlts();
487:
488: // make sure unreachable alts are as expected
489: if (expectingUnreachableAlts != null) {
490: BitSet s = new BitSet();
491: s.addAll(expectingUnreachableAlts);
492: BitSet s2 = new BitSet();
493: s2.addAll(unreachableAlts);
494: assertEquals("unreachable alts mismatch", s, s2);
495: } else {
496: assertEquals("unreachable alts mismatch", 0,
497: unreachableAlts.size());
498: }
499:
500: // check conflicting input
501: if (expectingAmbigInput != null) {
502: // first, find nondet message
503: Message msg = (Message) equeue.warnings.get(0);
504: assertTrue("expecting nondeterminism; found "
505: + msg.getClass().getName(),
506: msg instanceof GrammarNonDeterminismMessage);
507: GrammarNonDeterminismMessage nondetMsg = getNonDeterminismMessage(equeue.warnings);
508: List labels = nondetMsg.probe
509: .getSampleNonDeterministicInputSequence(nondetMsg.problemState);
510: String input = nondetMsg.probe
511: .getInputSequenceDisplay(labels);
512: assertEquals(expectingAmbigInput, input);
513: }
514:
515: // check nondet alts
516: if (expectingNonDetAlts != null) {
517: GrammarNonDeterminismMessage nondetMsg = getNonDeterminismMessage(equeue.warnings);
518: assertNotNull("found no nondet alts; expecting: "
519: + str(expectingNonDetAlts), nondetMsg);
520: List nonDetAlts = nondetMsg.probe
521: .getNonDeterministicAltsForState(nondetMsg.problemState);
522: // compare nonDetAlts with expectingNonDetAlts
523: BitSet s = new BitSet();
524: s.addAll(expectingNonDetAlts);
525: BitSet s2 = new BitSet();
526: s2.addAll(nonDetAlts);
527: assertEquals("nondet alts mismatch", s, s2);
528: } else {
529: // not expecting any nondet alts, make sure there are none
530: GrammarNonDeterminismMessage nondetMsg = getNonDeterminismMessage(equeue.warnings);
531: assertNull("found nondet alts, but expecting none",
532: nondetMsg);
533: }
534:
535: assertEquals(expecting, result);
536: }
537:
538: protected GrammarNonDeterminismMessage getNonDeterminismMessage(
539: List warnings) {
540: for (int i = 0; i < warnings.size(); i++) {
541: Message m = (Message) warnings.get(i);
542: if (m instanceof GrammarNonDeterminismMessage) {
543: return (GrammarNonDeterminismMessage) m;
544: }
545: }
546: return null;
547: }
548:
549: protected String str(int[] elements) {
550: StringBuffer buf = new StringBuffer();
551: for (int i = 0; i < elements.length; i++) {
552: if (i > 0) {
553: buf.append(", ");
554: }
555: int element = elements[i];
556: buf.append(element);
557: }
558: return buf.toString();
559: }
560: }
|