Package org.antlr.test

Source Code of org.antlr.test.TestSemanticPredicates

/*
* [The "BSD license"]
*  Copyright (c) 2010 Terence Parr
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*  1. Redistributions of source code must retain the above copyright
*      notice, this list of conditions and the following disclaimer.
*  2. Redistributions in binary form must reproduce the above copyright
*      notice, this list of conditions and the following disclaimer in the
*      documentation and/or other materials provided with the distribution.
*  3. The name of the author may not be used to endorse or promote products
*      derived from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
*  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
*  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
*  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.test;

import org.antlr.analysis.DFA;
import org.antlr.analysis.DecisionProbe;
import org.antlr.analysis.Label;
import org.antlr.codegen.CodeGenerator;
import org.antlr.misc.BitSet;
import org.antlr.runtime.Token;
import org.antlr.tool.*;
import org.junit.Test;

import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;

public class TestSemanticPredicates extends BaseTest {

  /** Public default constructor used by TestRig */
  public TestSemanticPredicates() {
  }

  @Test public void testPredsButSyntaxResolves() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {p1}? A | {p2}? B ;");
    String expecting =
      ".s0-A->:s1=>1\n" +
      ".s0-B->:s2=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testLL_1_Pred() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {p1}? A | {p2}? A ;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{p2}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testLL_1_Pred_forced_k_1() throws Exception {
    // should stop just like before w/o k set.
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a options {k=1;} : {p1}? A | {p2}? A ;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{p2}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testLL_2_Pred() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {p1}? A B | {p2}? A B ;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-B->.s2\n" +
      ".s2-{p1}?->:s3=>1\n" +
      ".s2-{p2}?->:s4=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testPredicatedLoop() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : ( {p1}? A | {p2}? A )+;");
    String expecting =                   // loop back
      ".s0-A->.s2\n" +
      ".s0-EOF->:s1=>3\n" +
      ".s2-{p1}?->:s3=>1\n" +
      ".s2-{p2}?->:s4=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testPredicatedToStayInLoop() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : ( {p1}? A )+ (A)+;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{true}?->:s3=>2\n";       // loop back
        checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testAndPredicates() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {p1}? {p1a}? A | {p2}? A ;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-{(p1a&&p1)}?->:s2=>1\n" +
      ".s1-{p2}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test
    public void testOrPredicates() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : b | {p2}? A ;\n" +
      "b : {p1}? A | {p1a}? A ;");
    String expecting =
      ".s0-A->.s1\n" +
            ".s1-{(p1a||p1)}?->:s2=>1\n" +
            ".s1-{p2}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testIgnoresHoistingDepthGreaterThanZero() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : A {p1}? | A {p2}?;");
    String expecting =
      ".s0-A->:s1=>1\n";
    checkDecision(g, 1, expecting, new int[] {2},
            new int[] {1,2}, "A", null, null, 2, false);
  }

  @Test public void testIgnoresPredsHiddenByActions() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {a1} {p1}? A | {a2} {p2}? A ;");
    String expecting =
      ".s0-A->:s1=>1\n";
    checkDecision(g, 1, expecting, new int[] {2},
            new int[] {1,2}, "A", null, null, 2, true);
  }

  @Test public void testIgnoresPredsHiddenByActionsOneAlt() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {p1}? A | {a2} {p2}? A ;"); // ok since 1 pred visible
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{true}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null,
            null, null, null, null, 0, true);
  }

  /*
  @Test public void testIncompleteSemanticHoistedContextk2() throws Exception {
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : b | A B;\n" +
      "b : {p1}? A B | A B ;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-B->:s2=>1\n";
    checkDecision(g, 1, expecting, new int[] {2},
            new int[] {1,2}, "A B", new int[] {1}, null, 3);
  } 
   */

  @Test public void testHoist2() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : b | c ;\n" +
      "b : {p1}? A ;\n" +
      "c : {p2}? A ;\n");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{p2}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testHoistCorrectContext() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : b | {p2}? ID ;\n" +
      "b : {p1}? ID | INT ;\n");
    String expecting =  // only tests after ID, not INT :)
      ".s0-ID->.s1\n" +
      ".s0-INT->:s2=>1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{p2}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testDefaultPredNakedAltIsLast() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : b | ID ;\n" +
      "b : {p1}? ID | INT ;\n");
    String expecting =
      ".s0-ID->.s1\n" +
      ".s0-INT->:s2=>1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{true}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testDefaultPredNakedAltNotLast() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : ID | b ;\n" +
      "b : {p1}? ID | INT ;\n");
    String expecting =
      ".s0-ID->.s1\n" +
      ".s0-INT->:s3=>2\n" +
      ".s1-{!(p1)}?->:s2=>1\n" +
      ".s1-{p1}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testLeftRecursivePred() throws Exception {
    // No analysis possible. but probably good to fail.  Not sure we really want
    // left-recursion even if guarded with pred.
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "s : a ;\n" +
      "a : {p1}? a | ID ;\n");
    String expecting =
      ".s0-ID->.s1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{true}?->:s3=>2\n";

    DecisionProbe.verbose=true; // make sure we get all error info
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    CodeGenerator generator = new CodeGenerator(newTool(), g, "Java");
    g.setCodeGenerator(generator);
    if ( g.getNumberOfDecisions()==0 ) {
      g.buildNFA();
      g.createLookaheadDFAs(false);
    }

    DFA dfa = g.getLookaheadDFA(1);
    assertEquals(null, dfa); // can't analyze.

    /*
    String result = serializer.serialize(dfa.startState);
    assertEquals(expecting, result);
    */

    assertEquals("unexpected number of expected problems", 1, equeue.size());
    Message msg = equeue.errors.get(0);
    assertTrue("warning must be a left recursion msg",
            msg instanceof LeftRecursionCyclesMessage);
  }

  @Test public void testIgnorePredFromLL2AltLastAltIsDefaultTrue() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {p1}? A B | A C | {p2}? A | {p3}? A | A ;\n");
    // two situations of note:
    // 1. A B syntax is enough to predict that alt, so p1 is not used
    //    to distinguish it from alts 2..5
    // 2. Alts 3, 4, 5 are nondeterministic with upon A.  p2, p3 and the
    //    complement of p2||p3 is sufficient to resolve the conflict. Do
    //    not include alt 1's p1 pred in the "complement of other alts"
    //    because it is not considered nondeterministic with alts 3..5
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-B->:s2=>1\n" +
      ".s1-C->:s3=>2\n" +
      ".s1-{p2}?->:s4=>3\n" +
      ".s1-{p3}?->:s5=>4\n" +
      ".s1-{true}?->:s6=>5\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testIgnorePredFromLL2AltPredUnionNeeded() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : {p1}? A B | A C | {p2}? A | A | {p3}? A ;\n");
    // two situations of note:
    // 1. A B syntax is enough to predict that alt, so p1 is not used
    //    to distinguish it from alts 2..5
    // 2. Alts 3, 4, 5 are nondeterministic with upon A.  p2, p3 and the
    //    complement of p2||p3 is sufficient to resolve the conflict. Do
    //    not include alt 1's p1 pred in the "complement of other alts"
    //    because it is not considered nondeterministic with alts 3..5
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-B->:s2=>1\n" +
      ".s1-C->:s3=>2\n" +
      ".s1-{!((p3||p2))}?->:s5=>4\n" +
      ".s1-{p2}?->:s4=>3\n" +
      ".s1-{p3}?->:s6=>5\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testPredGets2SymbolSyntacticContext() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : b | A B | C ;\n" +
      "b : {p1}? A B ;\n");
    String expecting =
      ".s0-A->.s1\n" +
      ".s0-C->:s5=>3\n" +
      ".s1-B->.s2\n" +
      ".s2-{p1}?->:s3=>1\n" +
      ".s2-{true}?->:s4=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testMatchesLongestThenTestPred() throws Exception {
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "a : b | c ;\n" +
      "b : {p}? A ;\n" +
      "c : {q}? (A|B)+ ;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s0-B->:s3=>2\n" +
      ".s1-{p}?->:s2=>1\n" +
      ".s1-{q}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testPredsUsedAfterRecursionOverflow() throws Exception {
    // analysis must bail out due to non-LL(*) nature (ovf)
    // retries with k=1 (but with LL(*) algorithm not optimized version
    // as it has preds)
    Grammar g = new Grammar(
      "parser grammar P;\n"+
      "s : {p1}? e '.' | {p2}? e ':' ;\n" +
      "e : '(' e ')' | INT ;\n");
    String expecting =
      ".s0-'('->.s1\n" +
      ".s0-INT->.s4\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{p2}?->:s3=>2\n" +
      ".s4-{p1}?->:s2=>1\n" +
      ".s4-{p2}?->:s3=>2\n";
    DecisionProbe.verbose=true; // make sure we get all error info
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    CodeGenerator generator = new CodeGenerator(newTool(), g, "Java");
    g.setCodeGenerator(generator);
    if ( g.getNumberOfDecisions()==0 ) {
      g.buildNFA();
      g.createLookaheadDFAs(false);
    }

    assertEquals("unexpected number of expected problems", 0, equeue.size());
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testPredsUsedAfterK2FailsNoRecursionOverflow() throws Exception {
    // analysis must bail out due to non-LL(*) nature (ovf)
    // retries with k=1 (but with LL(*) algorithm not optimized version
    // as it has preds)
    Grammar g = new Grammar(
      "grammar P;\n" +
      "options {k=2;}\n"+
      "s : {p1}? e '.' | {p2}? e ':' ;\n" +
      "e : '(' e ')' | INT ;\n");
    String expecting =
      ".s0-'('->.s1\n" +
      ".s0-INT->.s6\n" +
      ".s1-'('->.s2\n" +
      ".s1-INT->.s5\n" +
      ".s2-{p1}?->:s3=>1\n" +
      ".s2-{p2}?->:s4=>2\n" +
      ".s5-{p1}?->:s3=>1\n" +
      ".s5-{p2}?->:s4=>2\n" +
      ".s6-'.'->:s3=>1\n" +
      ".s6-':'->:s4=>2\n";
    DecisionProbe.verbose=true; // make sure we get all error info
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    CodeGenerator generator = new CodeGenerator(newTool(), g, "Java");
    g.setCodeGenerator(generator);
    if ( g.getNumberOfDecisions()==0 ) {
      g.buildNFA();
      g.createLookaheadDFAs(false);
    }

    assertEquals("unexpected number of expected problems", 0, equeue.size());
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testLexerMatchesLongestThenTestPred() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar P;\n"+
      "B : {p}? 'a' ;\n" +
      "C : {q}? ('a'|'b')+ ;");
    String expecting =
      ".s0-'a'->.s1\n" +
      ".s0-'b'->:s4=>2\n" +
      ".s1-'a'..'b'->:s4=>2\n" +
      ".s1-<EOT>->.s2\n" +
      ".s2-{p}?->:s3=>1\n" +
      ".s2-{q}?->:s4=>2\n";
    checkDecision(g, 2, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testLexerMatchesLongestMinusPred() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar P;\n"+
      "B : 'a' ;\n" +
      "C : ('a'|'b')+ ;");
    String expecting =
      ".s0-'a'->.s1\n" +
      ".s0-'b'->:s3=>2\n" +
      ".s1-'a'..'b'->:s3=>2\n" +
      ".s1-<EOT>->:s2=>1\n";
    checkDecision(g, 2, expecting, null, null, null, null, null, 0, false);
  }

    @Test
    public void testGatedPred() throws Exception {
    // gated preds are present on all arcs in predictor
    Grammar g = new Grammar(
      "lexer grammar P;\n"+
      "B : {p}? => 'a' ;\n" +
      "C : {q}? => ('a'|'b')+ ;");
    String expecting =
      ".s0-'a'&&{(q||p)}?->.s1\n" +
            ".s0-'b'&&{q}?->:s4=>2\n" +
            ".s1-'a'..'b'&&{q}?->:s4=>2\n" +
            ".s1-<EOT>&&{(q||p)}?->.s2\n" +
            ".s2-{p}?->:s3=>1\n" +
            ".s2-{q}?->:s4=>2\n";
    checkDecision(g, 2, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testGatedPredHoistsAndCanBeInStopState() throws Exception {
    // I found a bug where merging stop states made us throw away
    // a stop state with a gated pred!
    Grammar g = new Grammar(
      "grammar u;\n" +
      "a : b+ ;\n" +
      "b : 'x' | {p}?=> 'y' ;");
    String expecting =
      ".s0-'x'->:s2=>1\n" +
      ".s0-'y'&&{p}?->:s3=>1\n" +
      ".s0-EOF->:s1=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test
    public void testGatedPredInCyclicDFA() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar P;\n"+
      "A : {p}?=> ('a')+ 'x' ;\n" +
      "B : {q}?=> ('a'|'b')+ 'x' ;");
    String expecting =
      ".s0-'a'&&{(q||p)}?->.s1\n" +
            ".s0-'b'&&{q}?->:s5=>2\n" +
            ".s1-'a'&&{(q||p)}?->.s1\n" +
            ".s1-'b'&&{q}?->:s5=>2\n" +
            ".s1-'x'&&{(q||p)}?->.s2\n" +
            ".s2-<EOT>&&{(q||p)}?->.s3\n" +
            ".s3-{p}?->:s4=>1\n" +
            ".s3-{q}?->:s5=>2\n";
    checkDecision(g, 3, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testGatedPredNotActuallyUsedOnEdges() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar P;\n"+
      "A : ('a' | {p}?=> 'a')\n" +
      "  | 'a' 'b'\n" +
      "  ;");
    String expecting1 =
      ".s0-'a'->.s1\n" +
      ".s1-{!(p)}?->:s2=>1\n" +    // Used to disambig subrule
      ".s1-{p}?->:s3=>2\n";
    // rule A decision can't test p from s0->1 because 'a' is valid
    // for alt1 *and* alt2 w/o p.  Can't test p from s1 to s3 because
    // we might have passed the first alt of subrule.  The same state
    // is listed in s2 in 2 different configurations: one with and one
    // w/o p.  Can't test therefore.  p||true == true.
    String expecting2 =
      ".s0-'a'->.s1\n" +
      ".s1-'b'->:s2=>2\n" +
      ".s1-<EOT>->:s3=>1\n";
    checkDecision(g, 1, expecting1, null, null, null, null, null, 0, false);
    checkDecision(g, 2, expecting2, null, null, null, null, null, 0, false);
  }

  @Test public void testGatedPredDoesNotForceAllToBeGated() throws Exception {
    Grammar g = new Grammar(
      "grammar w;\n" +
      "a : b | c ;\n" +
      "b : {p}? B ;\n" +
      "c : {q}?=> d ;\n" +
      "d : {r}? C ;\n");
    String expecting =
      ".s0-B->:s1=>1\n" +
      ".s0-C&&{q}?->:s2=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testGatedPredDoesNotForceAllToBeGated2() throws Exception {
    Grammar g = new Grammar(
      "grammar w;\n" +
      "a : b | c ;\n" +
      "b : {p}? B ;\n" +
      "c : {q}?=> d ;\n" +
      "d : {r}?=> C\n" +
      "  | B\n" +
      "  ;\n");
    String expecting =
      ".s0-B->.s1\n" +
      ".s0-C&&{(r&&q)}?->:s3=>2\n" +
      ".s1-{p}?->:s2=>1\n" +
      ".s1-{q}?->:s3=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  @Test public void testORGatedPred() throws Exception {
    Grammar g = new Grammar(
      "grammar w;\n" +
      "a : b | c ;\n" +
      "b : {p}? B ;\n" +
      "c : {q}?=> d ;\n" +
      "d : {r}?=> C\n" +
      "  | {s}?=> B\n" +
      "  ;\n");
    String expecting =
      ".s0-B->.s1\n" +
      ".s0-C&&{(r&&q)}?->:s3=>2\n" +
      ".s1-{(s&&q)}?->:s3=>2\n" +
      ".s1-{p}?->:s2=>1\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  /** The following grammar should yield an error that rule 'a' has
   *  insufficient semantic info pulled from 'b'.
   */
  @Test public void testIncompleteSemanticHoistedContext() throws Exception {
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : b | B;\n" +
      "b : {p1}? B | B ;");
    String expecting =
      ".s0-B->:s1=>1\n";
    checkDecision(g, 1, expecting, new int[] {2},
            new int[] {1,2}, "B", new int[] {1}, null, 3, false);
  }

  @Test public void testIncompleteSemanticHoistedContextk2() throws Exception {
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : b | A B;\n" +
      "b : {p1}? A B | A B ;");
    String expecting =
      ".s0-A->.s1\n" +
      ".s1-B->:s2=>1\n";
    checkDecision(g, 1, expecting, new int[] {2},
            new int[] {1,2}, "A B", new int[] {1}, null, 3, false);
  }

  @Test public void testIncompleteSemanticHoistedContextInFOLLOW() throws Exception {
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "options {k=1;}\n" + // limit to k=1 because it's LL(2); force pred hoist
      "a : A? ;\n" + // need FOLLOW
      "b : X a {p1}? A | Y a A ;"); // only one A is covered
    String expecting =
      ".s0-A->:s1=>1\n"; // s0-EOF->s2 branch pruned during optimization
    checkDecision(g, 1, expecting, new int[] {2},
            new int[] {1,2}, "A", new int[] {2}, null, 3, false);
  }

  @Test public void testIncompleteSemanticHoistedContextInFOLLOWk2() throws Exception {
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : (A B)? ;\n" + // need FOLLOW
      "b : X a {p1}? A B | Y a A B | Z a ;"); // only first alt is covered
    String expecting =
      ".s0-A->.s1\n" +
      ".s0-EOF->:s3=>2\n" +
      ".s1-B->:s2=>1\n";
    checkDecision(g, 1, expecting, null,
            new int[] {1,2}, "A B", new int[] {2}, null, 2, false);
  }

  @Test public void testIncompleteSemanticHoistedContextInFOLLOWDueToHiddenPred() throws Exception {
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : (A B)? ;\n" + // need FOLLOW
      "b : X a {p1}? A B | Y a {a1} {p2}? A B | Z a ;"); // only first alt is covered
    String expecting =
      ".s0-A->.s1\n" +
      ".s0-EOF->:s3=>2\n" +
      ".s1-B->:s2=>1\n";
    checkDecision(g, 1, expecting, null,
            new int[] {1,2}, "A B", new int[] {2}, null, 2, true);
  }

  /** The following grammar should yield an error that rule 'a' has
   *  insufficient semantic info pulled from 'b'.  This is the same
   *  as the previous case except that the D prevents the B path from
   *  "pinching" together into a single NFA state.
   *
   *  This test also demonstrates that just because B D could predict
   *  alt 1 in rule 'a', it is unnecessary to continue NFA&rarr;DFA
   *  conversion to include an edge for D.  Alt 1 is the only possible
   *  prediction because we resolve the ambiguity by choosing alt 1.
   */
  @Test public void testIncompleteSemanticHoistedContext2() throws Exception {
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : b | B;\n" +
      "b : {p1}? B | B D ;");
    String expecting =
      ".s0-B->:s1=>1\n";
    checkDecision(g, 1, expecting, new int[] {2},
            new int[] {1,2}, "B", new int[] {1},
            null, 3, false);
  }

  @Test public void testTooFewSemanticPredicates() throws Exception {
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : {p1}? A | A | A ;");
    String expecting =
      ".s0-A->:s1=>1\n";
    checkDecision(g, 1, expecting, new int[] {2,3},
            new int[] {1,2,3}, "A",
            null, null, 2, false);
  }

  @Test public void testPredWithK1() throws Exception {
    Grammar g = new Grammar(
      "\tlexer grammar TLexer;\n" +
      "A\n" +
      "options {\n" +
      "  k=1;\n" +
      "}\n" +
      "  : {p1}? ('x')+ '.'\n" +
      "  | {p2}? ('x')+ '.'\n" +
      "  ;\n");
    String expecting =
      ".s0-'x'->.s1\n" +
      ".s1-{p1}?->:s2=>1\n" +
      ".s1-{p2}?->:s3=>2\n";
    int[] unreachableAlts = null;
    int[] nonDetAlts = null;
    String ambigInput = null;
    int[] insufficientPredAlts = null;
    int[] danglingAlts = null;
    int numWarnings = 0;
    checkDecision(g, 3, expecting, unreachableAlts,
            nonDetAlts, ambigInput, insufficientPredAlts,
            danglingAlts, numWarnings, false);
  }

  @Test public void testPredWithArbitraryLookahead() throws Exception {
    Grammar g = new Grammar(
      "\tlexer grammar TLexer;\n" +
      "A : {p1}? ('x')+ '.'\n" +
      "  | {p2}? ('x')+ '.'\n" +
      "  ;\n");
    String expecting =
      ".s0-'x'->.s1\n" +
      ".s1-'.'->.s2\n" +
      ".s1-'x'->.s1\n" +
      ".s2-{p1}?->:s3=>1\n" +
      ".s2-{p2}?->:s4=>2\n";
    int[] unreachableAlts = null;
    int[] nonDetAlts = null;
    String ambigInput = null;
    int[] insufficientPredAlts = null;
    int[] danglingAlts = null;
    int numWarnings = 0;
    checkDecision(g, 3, expecting, unreachableAlts,
            nonDetAlts, ambigInput, insufficientPredAlts,
            danglingAlts, numWarnings, false);
  }

  @Test
    /** For a DFA state with lots of configurations that have the same
   *  predicate, don't just OR them all together as it's a waste to
   *  test a||a||b||a||a etc...  ANTLR makes a unique set and THEN
   *  OR's them together.
   */
    public void testUniquePredicateOR() throws Exception {
    Grammar g = new Grammar(
      "parser grammar v;\n" +
      "\n" +
      "a : {a}? b\n" +
      "  | {b}? b\n" +
      "  ;\n" +
      "\n" +
      "b : {c}? (X)+ ;\n" +
      "\n" +
      "c : a\n" +
      "  | b\n" +
      "  ;\n");
    String expecting =
      ".s0-X->.s1\n" +
            ".s1-{((b||a)&&c)}?->:s2=>1\n" +
            ".s1-{c}?->:s3=>2\n";
    int[] unreachableAlts = null;
    int[] nonDetAlts = null;
    String ambigInput = null;
    int[] insufficientPredAlts = null;
    int[] danglingAlts = null;
    int numWarnings = 0;
    checkDecision(g, 3, expecting, unreachableAlts,
            nonDetAlts, ambigInput, insufficientPredAlts,
            danglingAlts, numWarnings, false);
  }

    @Test
    public void testSemanticContextPreventsEarlyTerminationOfClosure() throws Exception {
    Grammar g = new Grammar(
      "parser grammar T;\n" +
      "a : loop SEMI | ID SEMI\n" +
      "  ;\n" +
      "loop\n" +
      "    : {while}? ID\n" +
      "    | {do}? ID\n" +
      "    | {for}? ID\n" +
      "    ;");
    String expecting =
      ".s0-ID->.s1\n" +
            ".s1-SEMI->.s2\n" +
            ".s2-{(for||do||while)}?->:s3=>1\n" +
            ".s2-{true}?->:s4=>2\n";
    checkDecision(g, 1, expecting, null, null, null, null, null, 0, false);
  }

  // S U P P O R T

  public void _template() throws Exception {
    Grammar g = new Grammar(
      "parser grammar t;\n"+
      "a : A | B;");
    String expecting =
      "\n";
    int[] unreachableAlts = null;
    int[] nonDetAlts = new int[] {1,2};
    String ambigInput = "L ID R";
    int[] insufficientPredAlts = new int[] {1};
    int[] danglingAlts = null;
    int numWarnings = 1;
    checkDecision(g, 1, expecting, unreachableAlts,
            nonDetAlts, ambigInput, insufficientPredAlts,
            danglingAlts, numWarnings, false);
  }

  protected void checkDecision(Grammar g,
                 int decision,
                 String expecting,
                 int[] expectingUnreachableAlts,
                 int[] expectingNonDetAlts,
                 String expectingAmbigInput,
                 int[] expectingInsufficientPredAlts,
                 int[] expectingDanglingAlts,
                 int expectingNumWarnings,
                 boolean hasPredHiddenByAction)
    throws Exception
  {
    DecisionProbe.verbose=true; // make sure we get all error info
    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);
    CodeGenerator generator = new CodeGenerator(newTool(), g, "Java");
    g.setCodeGenerator(generator);
    // mimic actions of org.antlr.Tool first time for grammar g
    if ( g.getNumberOfDecisions()==0 ) {
      g.buildNFA();
      g.createLookaheadDFAs(false);
    }

    if ( equeue.size()!=expectingNumWarnings ) {
      System.err.println("Warnings issued: "+equeue);
    }

    assertEquals("unexpected number of expected problems",
           expectingNumWarnings, equeue.size());

    DFA dfa = g.getLookaheadDFA(decision);
    FASerializer serializer = new FASerializer(g);
    String result = serializer.serialize(dfa.startState);
    //System.out.print(result);
    List<Integer> unreachableAlts = dfa.getUnreachableAlts();

    // make sure unreachable alts are as expected
    if ( expectingUnreachableAlts!=null ) {
      BitSet s = new BitSet();
      s.addAll(expectingUnreachableAlts);
      BitSet s2 = new BitSet();
      s2.addAll(unreachableAlts);
      assertEquals("unreachable alts mismatch", s, s2);
    }
    else {
      assertEquals("unreachable alts mismatch", 0,
             unreachableAlts!=null?unreachableAlts.size():0);
    }

    // check conflicting input
    if ( expectingAmbigInput!=null ) {
      // first, find nondet message
      Message msg = getNonDeterminismMessage(equeue.warnings);
      assertNotNull("no nondeterminism warning?", msg);
      assertTrue("expecting nondeterminism; found "+msg.getClass().getName(),
      msg instanceof GrammarNonDeterminismMessage);
      GrammarNonDeterminismMessage nondetMsg =
        getNonDeterminismMessage(equeue.warnings);
      List<Label> labels =
        nondetMsg.probe.getSampleNonDeterministicInputSequence(nondetMsg.problemState);
      String input = nondetMsg.probe.getInputSequenceDisplay(labels);
      assertEquals(expectingAmbigInput, input);
    }

    // check nondet alts
    if ( expectingNonDetAlts!=null ) {
      GrammarNonDeterminismMessage nondetMsg =
        getNonDeterminismMessage(equeue.warnings);
      assertNotNull("found no nondet alts; expecting: "+
                    str(expectingNonDetAlts), nondetMsg);
      List<Integer> nonDetAlts =
        nondetMsg.probe.getNonDeterministicAltsForState(nondetMsg.problemState);
      // compare nonDetAlts with expectingNonDetAlts
      BitSet s = new BitSet();
      s.addAll(expectingNonDetAlts);
      BitSet s2 = new BitSet();
      s2.addAll(nonDetAlts);
      assertEquals("nondet alts mismatch", s, s2);
      assertEquals("mismatch between expected hasPredHiddenByAction", hasPredHiddenByAction,
             nondetMsg.problemState.dfa.hasPredicateBlockedByAction);
    }
    else {
      // not expecting any nondet alts, make sure there are none
      GrammarNonDeterminismMessage nondetMsg =
        getNonDeterminismMessage(equeue.warnings);
      assertNull("found nondet alts, but expecting none", nondetMsg);
    }

    if ( expectingInsufficientPredAlts!=null ) {
      GrammarInsufficientPredicatesMessage insuffPredMsg =
        getGrammarInsufficientPredicatesMessage(equeue.warnings);
      assertNotNull("found no GrammarInsufficientPredicatesMessage alts; expecting: "+
                    str(expectingNonDetAlts), insuffPredMsg);
      Map<Integer, Set<Token>> locations = insuffPredMsg.altToLocations;
      Set<Integer> actualAlts = locations.keySet();
      BitSet s = new BitSet();
      s.addAll(expectingInsufficientPredAlts);
      BitSet s2 = new BitSet();
      s2.addAll(actualAlts);
      assertEquals("mismatch between insufficiently covered alts", s, s2);
      assertEquals("mismatch between expected hasPredHiddenByAction", hasPredHiddenByAction,
             insuffPredMsg.problemState.dfa.hasPredicateBlockedByAction);
    }
    else {
      // not expecting any nondet alts, make sure there are none
      GrammarInsufficientPredicatesMessage nondetMsg =
        getGrammarInsufficientPredicatesMessage(equeue.warnings);
      if ( nondetMsg!=null ) {
        System.out.println(equeue.warnings);
      }
      assertNull("found insufficiently covered alts, but expecting none", nondetMsg);
    }

    assertEquals(expecting, result);
  }

  protected GrammarNonDeterminismMessage getNonDeterminismMessage(List<? extends Message> warnings) {
    for (int i = 0; i < warnings.size(); i++) {
      Message m = warnings.get(i);
      if ( m instanceof GrammarNonDeterminismMessage ) {
        return (GrammarNonDeterminismMessage)m;
      }
    }
    return null;
  }

  protected GrammarInsufficientPredicatesMessage getGrammarInsufficientPredicatesMessage(List<? extends Message> warnings) {
    for (int i = 0; i < warnings.size(); i++) {
      Message m = warnings.get(i);
      if ( m instanceof GrammarInsufficientPredicatesMessage ) {
        return (GrammarInsufficientPredicatesMessage)m;
      }
    }
    return null;
  }

  protected String str(int[] elements) {
    StringBuilder buf = new StringBuilder();
    for (int i = 0; i < elements.length; i++) {
      if ( i>0 ) {
        buf.append(", ");
      }
      int element = elements[i];
      buf.append(element);
    }
    return buf.toString();
  }
}
TOP

Related Classes of org.antlr.test.TestSemanticPredicates

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.