Package org.antlr.test

Source Code of org.antlr.test.TestCharDFAConversion

/*
* [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.DFAOptimizer;
import org.antlr.codegen.CodeGenerator;
import org.antlr.tool.*;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.*;

public class TestCharDFAConversion extends BaseTest {

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

  // R A N G E S  &  S E T S

  @Test public void testSimpleRangeVersusChar() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : 'a'..'z' '@' | 'k' '$' ;");
    g.createLookaheadDFAs();
    String expecting =
      ".s0-'k'->.s1\n" +
      ".s0-{'a'..'j', 'l'..'z'}->:s2=>1\n" +
      ".s1-'$'->:s3=>2\n" +
      ".s1-'@'->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testRangeWithDisjointSet() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : 'a'..'z' '@'\n" +
      "  | ('k'|'9'|'p') '$'\n" +
      "  ;\n");
    g.createLookaheadDFAs();
    // must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'}
    String expecting =
      ".s0-'9'->:s3=>2\n" +
      ".s0-{'a'..'j', 'l'..'o', 'q'..'z'}->:s2=>1\n" +
      ".s0-{'k', 'p'}->.s1\n" +
      ".s1-'$'->:s3=>2\n" +
      ".s1-'@'->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testDisjointSetCollidingWithTwoRanges() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : ('a'..'z'|'0'..'9') '@'\n" +
      "  | ('k'|'9'|'p') '$'\n" +
      "  ;\n");
    g.createLookaheadDFAs(false);
    // must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'} and 0..9
    // into 0..8
    String expecting =
      ".s0-{'0'..'8', 'a'..'j', 'l'..'o', 'q'..'z'}->:s2=>1\n" +
      ".s0-{'9', 'k', 'p'}->.s1\n" +
      ".s1-'$'->:s3=>2\n" +
      ".s1-'@'->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testDisjointSetCollidingWithTwoRangesCharsFirst() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : ('k'|'9'|'p') '$'\n" +
      "  | ('a'..'z'|'0'..'9') '@'\n" +
      "  ;\n");
    // must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'} and 0..9
    // into 0..8
    String expecting =
      ".s0-{'0'..'8', 'a'..'j', 'l'..'o', 'q'..'z'}->:s3=>2\n" +
      ".s0-{'9', 'k', 'p'}->.s1\n" +
      ".s1-'$'->:s2=>1\n" +
      ".s1-'@'->:s3=>2\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testDisjointSetCollidingWithTwoRangesAsSeparateAlts() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : 'a'..'z' '@'\n" +
      "  | 'k' '$'\n" +
      "  | '9' '$'\n" +
      "  | 'p' '$'\n" +
      "  | '0'..'9' '@'\n" +
      "  ;\n");
    // must break up a..z into {'a'..'j', 'l'..'o', 'q'..'z'} and 0..9
    // into 0..8
    String expecting =
      ".s0-'0'..'8'->:s8=>5\n" +
      ".s0-'9'->.s6\n" +
      ".s0-'k'->.s1\n" +
      ".s0-'p'->.s4\n" +
      ".s0-{'a'..'j', 'l'..'o', 'q'..'z'}->:s2=>1\n" +
      ".s1-'$'->:s3=>2\n" +
      ".s1-'@'->:s2=>1\n" +
      ".s4-'$'->:s5=>4\n" +
      ".s4-'@'->:s2=>1\n" +
      ".s6-'$'->:s7=>3\n" +
      ".s6-'@'->:s8=>5\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testKeywordVersusID() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "IF : 'if' ;\n" + // choose this over ID
      "ID : ('a'..'z')+ ;\n");
    String expecting =
      ".s0-'a'..'z'->:s2=>1\n" +
      ".s0-<EOT>->:s1=>2\n";
    checkDecision(g, 1, expecting, null);
    expecting =
      ".s0-'i'->.s1\n" +
      ".s0-{'a'..'h', 'j'..'z'}->:s4=>2\n" +
      ".s1-'f'->.s2\n" +
      ".s1-<EOT>->:s4=>2\n" +
      ".s2-'a'..'z'->:s4=>2\n" +
      ".s2-<EOT>->:s3=>1\n";
    checkDecision(g, 2, expecting, null);
  }

  @Test public void testIdenticalRules() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : 'a' ;\n" +
      "B : 'a' ;\n"); // can't reach this
    String expecting =
      ".s0-'a'->.s1\n" +
      ".s1-<EOT>->:s2=>1\n";

    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);

    checkDecision(g, 1, expecting, new int[] {2});

    assertEquals("unexpected number of expected problems",
            1, equeue.size());
    Message msg = equeue.errors.get(0);
    assertTrue("warning must be an unreachable alt",
            msg instanceof GrammarUnreachableAltsMessage);
    GrammarUnreachableAltsMessage u = (GrammarUnreachableAltsMessage)msg;
    assertEquals("[2]", u.alts.toString());

  }

  @Test public void testAdjacentNotCharLoops() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : (~'r')+ ;\n" +
      "B : (~'s')+ ;\n");
    String expecting =
      ".s0-'r'->:s3=>2\n" +
      ".s0-'s'->:s2=>1\n" +
      ".s0-{'\\u0000'..'q', 't'..'\\uFFFF'}->.s1\n" +
      ".s1-'r'->:s3=>2\n" +
      ".s1-<EOT>->:s2=>1\n" +
      ".s1-{'\\u0000'..'q', 't'..'\\uFFFF'}->.s1\n";
    checkDecision(g, 3, expecting, null);
  }

  @Test public void testNonAdjacentNotCharLoops() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : (~'r')+ ;\n" +
      "B : (~'t')+ ;\n");
    String expecting =
      ".s0-'r'->:s3=>2\n" +
      ".s0-'t'->:s2=>1\n" +
      ".s0-{'\\u0000'..'q', 's', 'u'..'\\uFFFF'}->.s1\n" +
      ".s1-'r'->:s3=>2\n" +
      ".s1-<EOT>->:s2=>1\n" +
      ".s1-{'\\u0000'..'q', 's', 'u'..'\\uFFFF'}->.s1\n";
    checkDecision(g, 3, expecting, null);
  }

  @Test public void testLoopsWithOptimizedOutExitBranches() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "A : 'x'* ~'x'+ ;\n");
    String expecting =
      ".s0-'x'->:s1=>1\n" +
      ".s0-{'\\u0000'..'w', 'y'..'\\uFFFF'}->:s2=>2\n";
    checkDecision(g, 1, expecting, null);

    // The optimizer yanks out all exit branches from EBNF blocks
    // This is ok because we've already verified there are no problems
    // with the enter/exit decision
    DFAOptimizer optimizer = new DFAOptimizer(g);
    optimizer.optimize();
    FASerializer serializer = new FASerializer(g);
    DFA dfa = g.getLookaheadDFA(1);
    String result = serializer.serialize(dfa.startState);
    expecting = ".s0-'x'->:s1=>1\n";
    assertEquals(expecting, result);
  }

  // N O N G R E E D Y

  @Test public void testNonGreedy() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "CMT : '/*' ( options {greedy=false;} : . )* '*/' ;");
    String expecting =
      ".s0-'*'->.s1\n" +
      ".s0-{'\\u0000'..')', '+'..'\\uFFFF'}->:s3=>1\n" +
      ".s1-'/'->:s2=>2\n" +
      ".s1-{'\\u0000'..'.', '0'..'\\uFFFF'}->:s3=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonGreedyWildcardStar() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "SLCMT : '//' ( options {greedy=false;} : . )* '\n' ;");
    String expecting =
      ".s0-'\\n'->:s1=>2\n" +
      ".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonGreedyByDefaultWildcardStar() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "SLCMT : '//' .* '\n' ;");
    String expecting =
      ".s0-'\\n'->:s1=>2\n" +
      ".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonGreedyWildcardPlus() throws Exception {
    // same DFA as nongreedy .* but code gen checks number of
    // iterations at runtime
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "SLCMT : '//' ( options {greedy=false;} : . )+ '\n' ;");
    String expecting =
      ".s0-'\\n'->:s1=>2\n" +
      ".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonGreedyByDefaultWildcardPlus() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "SLCMT : '//' .+ '\n' ;");
    String expecting =
      ".s0-'\\n'->:s1=>2\n" +
      ".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonGreedyByDefaultWildcardPlusWithParens() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "SLCMT : '//' (.)+ '\n' ;");
    String expecting =
      ".s0-'\\n'->:s1=>2\n" +
      ".s0-{'\\u0000'..'\\t', '\\u000B'..'\\uFFFF'}->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonWildcardNonGreedy() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "DUH : (options {greedy=false;}:'x'|'y')* 'xy' ;");
    String expecting =
      ".s0-'x'->.s1\n" +
      ".s0-'y'->:s4=>2\n" +
      ".s1-'x'->:s3=>1\n" +
      ".s1-'y'->:s2=>3\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonWildcardEOTMakesItWorkWithoutNonGreedyOption() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "DUH : ('x'|'y')* 'xy' ;");
    String expecting =
      ".s0-'x'->.s1\n" +
      ".s0-'y'->:s4=>1\n" +
      ".s1-'x'->:s4=>1\n" +
      ".s1-'y'->.s2\n" +
      ".s2-'x'..'y'->:s4=>1\n" +
      ".s2-<EOT>->:s3=>2\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testAltConflictsWithLoopThenExit() throws Exception {
    // \" predicts alt 1, but wildcard then " can predict exit also
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "STRING : '\"' (options {greedy=false;}: '\\\\\"' | .)* '\"' ;\n"
    );
    String expecting =
      ".s0-'\"'->:s1=>3\n" +
        ".s0-'\\\\'->.s2\n" +
        ".s0-{'\\u0000'..'!', '#'..'[', ']'..'\\uFFFF'}->:s4=>2\n" +
        ".s2-'\"'->:s3=>1\n" +
        ".s2-{'\\u0000'..'!', '#'..'\\uFFFF'}->:s4=>2\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNonGreedyLoopThatNeverLoops() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar t;\n"+
      "DUH : (options {greedy=false;}:'x')+ ;"); // loop never matched
    String expecting =
      ":s0=>2\n";

    ErrorQueue equeue = new ErrorQueue();
    ErrorManager.setErrorListener(equeue);

    checkDecision(g, 1, expecting, new int[] {1});

    assertEquals("unexpected number of expected problems",
            1, equeue.size());
    Message msg = equeue.errors.get(0);
    assertTrue("warning must be an unreachable alt",
           msg instanceof GrammarUnreachableAltsMessage);
    GrammarUnreachableAltsMessage u = (GrammarUnreachableAltsMessage)msg;
    assertEquals("[1]", u.alts.toString());
  }

  @Test public void testRecursive() throws Exception {
    // this is cool because the 3rd alt includes !(all other possibilities)
    Grammar g = new Grammar(
      "lexer grammar duh;\n" +
      "SUBTEMPLATE\n" +
      "        :       '{'\n" +
      "                ( SUBTEMPLATE\n" +
      "                | ESC\n" +
      "                | ~('}'|'\\\\'|'{')\n" +
      "                )*\n" +
      "                '}'\n" +
      "        ;\n" +
      "fragment\n" +
      "ESC     :       '\\\\' . ;");
    g.createLookaheadDFAs();
    String expecting =
      ".s0-'\\\\'->:s2=>2\n" +
      ".s0-'{'->:s1=>1\n" +
      ".s0-'}'->:s4=>4\n" +
      ".s0-{'\\u0000'..'[', ']'..'z', '|', '~'..'\\uFFFF'}->:s3=>3\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testRecursive2() throws Exception {
    // this is also cool because it resolves \\ to be ESC alt; it's just
    // less efficient of a DFA
    Grammar g = new Grammar(
      "lexer grammar duh;\n" +
      "SUBTEMPLATE\n" +
      "        :       '{'\n" +
      "                ( SUBTEMPLATE\n" +
      "                | ESC\n" +
      "                | ~('}'|'{')\n" +
      "                )*\n" +
      "                '}'\n" +
      "        ;\n" +
      "fragment\n" +
      "ESC     :       '\\\\' . ;");
    g.createLookaheadDFAs();
    String expecting =
      ".s0-'\\\\'->.s3\n" +
      ".s0-'{'->:s2=>1\n" +
      ".s0-'}'->:s1=>4\n" +
      ".s0-{'\\u0000'..'[', ']'..'z', '|', '~'..'\\uFFFF'}->:s5=>3\n" +
      ".s3-'\\\\'->:s8=>2\n" +
      ".s3-'{'->:s7=>2\n" +
      ".s3-'}'->.s4\n" +
      ".s3-{'\\u0000'..'[', ']'..'z', '|', '~'..'\\uFFFF'}->:s6=>2\n" +
      ".s4-'\\u0000'..'\\uFFFF'->:s6=>2\n" +
      ".s4-<EOT>->:s5=>3\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNotFragmentInLexer() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar T;\n"+
      "A : 'a' | ~B {;} ;\n" +
      "fragment B : 'a' ;\n");
    g.createLookaheadDFAs();
    String expecting =
      ".s0-'a'->:s1=>1\n" +
      ".s0-{'\\u0000'..'`', 'b'..'\\uFFFF'}->:s2=>2\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNotSetFragmentInLexer() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar T;\n"+
      "A : B | ~B {;} ;\n" +
      "fragment B : 'a'|'b' ;\n");
    g.createLookaheadDFAs();
    String expecting =
      ".s0-'a'..'b'->:s1=>1\n" +
      ".s0-{'\\u0000'..'`', 'c'..'\\uFFFF'}->:s2=>2\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNotTokenInLexer() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar T;\n"+
      "A : 'x' ('a' | ~B {;}) ;\n" +
      "B : 'a' ;\n");
    g.createLookaheadDFAs();
    String expecting =
      ".s0-'a'->:s1=>1\n" +
      ".s0-{'\\u0000'..'`', 'b'..'\\uFFFF'}->:s2=>2\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNotComplicatedSetRuleInLexer() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar T;\n"+
      "A : B | ~B {;} ;\n" +
      "fragment B : 'a'|'b'|'c'..'e'|C ;\n" +
      "fragment C : 'f' ;\n"); // has to seen from B to C
    String expecting =
      ".s0-'a'..'f'->:s1=>1\n" +
      ".s0-{'\\u0000'..'`', 'g'..'\\uFFFF'}->:s2=>2\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testNotSetWithRuleInLexer() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar T;\n"+
      "T : ~('a' | B) | 'a';\n" +
      "fragment\n" +
      "B : 'b' ;\n" +
      "C : ~'x'{;} ;"); // force Tokens to not collapse T|C
    String expecting =
      ".s0-'b'->:s3=>2\n" +
      ".s0-'x'->:s2=>1\n" +
      ".s0-{'\\u0000'..'a', 'c'..'w', 'y'..'\\uFFFF'}->.s1\n" +
      ".s1-<EOT>->:s2=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testSetCallsRuleWithNot() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar A;\n" +
      "T : ~'x' ;\n" +
      "S : 'x' (T | 'x') ;\n");
    String expecting =
      ".s0-'x'->:s2=>2\n" +
      ".s0-{'\\u0000'..'w', 'y'..'\\uFFFF'}->:s1=>1\n";
    checkDecision(g, 1, expecting, null);
  }

  @Test public void testSynPredInLexer() throws Exception {
    Grammar g = new Grammar(
      "lexer grammar T;\n"+
      "LT:  '<' ' '*\n" +
      "  |  ('<' IDENT) => '<' IDENT '>'\n" + // this was causing syntax error
      "  ;\n" +
      "IDENT:    'a'+;\n");
    // basically, Tokens rule should not do set compression test
    String expecting =
      ".s0-'<'->:s1=>1\n" +
      ".s0-'a'->:s2=>2\n";
    checkDecision(g, 4, expecting, null); // 4 is Tokens rule
  }

  // S U P P O R T

  public void _template() throws Exception {
    Grammar g = new Grammar(
      "grammar T;\n"+
      "a : A | B;");
    String expecting =
      "\n";
    checkDecision(g, 1, expecting, null);
  }

  protected void checkDecision(Grammar g,
                 int decision,
                 String expecting,
                 int[] expectingUnreachableAlts)
    throws Exception
  {

    // mimic actions of org.antlr.Tool first time for grammar g
    if ( g.getCodeGenerator()==null ) {
      CodeGenerator generator = new CodeGenerator(null, g, "Java");
      g.setCodeGenerator(generator);
      g.buildNFA();
      g.createLookaheadDFAs(false);
    }

    DFA dfa = g.getLookaheadDFA(decision);
    assertNotNull("unknown decision #"+decision, dfa);
    FASerializer serializer = new FASerializer(g);
    String result = serializer.serialize(dfa.startState);
    //System.out.print(result);
    List<Integer> nonDetAlts = dfa.getUnreachableAlts();
    //System.out.println("alts w/o predict state="+nonDetAlts);

    // first make sure nondeterministic alts are as expected
    if ( expectingUnreachableAlts==null ) {
      if ( nonDetAlts!=null && !nonDetAlts.isEmpty() ) {
        System.err.println("nondeterministic alts (should be empty): "+nonDetAlts);
      }
      assertEquals("unreachable alts mismatch", 0, nonDetAlts!=null?nonDetAlts.size():0);
    }
    else {
      for (int i=0; i<expectingUnreachableAlts.length; i++) {
        assertTrue("unreachable alts mismatch",
               nonDetAlts!=null?nonDetAlts.contains(new Integer(expectingUnreachableAlts[i])):false);
      }
    }
    assertEquals(expecting, result);
  }

}
TOP

Related Classes of org.antlr.test.TestCharDFAConversion

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.