forked from jasder/antlr
fetched runtime-tests-generator branch into master and did a manual merge. Fixes #76 as I was right there anyway.
This commit is contained in:
commit
29c2712ae1
|
@ -23,7 +23,7 @@
|
|||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-java</artifactId>
|
||||
<version>2.43.1</version>
|
||||
<version>2.44.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -1,820 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.antlr.v4.tool.ErrorType;
|
||||
import org.antlr.v4.tool.Grammar;
|
||||
import org.antlr.v4.tool.GrammarSemanticsMessage;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TestCompositeGrammars extends BaseTest {
|
||||
protected boolean debug = false;
|
||||
|
||||
@Test public void testImportFileLocationInSubdir() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : B {System.out.println(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
String subdir = tmpdir + "/sub";
|
||||
mkdir(subdir);
|
||||
writeFile(subdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
ErrorQueue equeue = antlr("M.g4", false, "-lib", subdir);
|
||||
assertEquals(equeue.size(), 0);
|
||||
}
|
||||
|
||||
@Test public void testImportFileNotSearchedForInOutputDir() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : B {System.out.println(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
String outdir = tmpdir + "/out";
|
||||
mkdir(outdir);
|
||||
writeFile(outdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
ErrorQueue equeue = antlr("M.g4", false, "-o", outdir);
|
||||
assertEquals(ErrorType.CANNOT_FIND_IMPORTED_GRAMMAR, equeue.errors.get(0).getErrorType());
|
||||
}
|
||||
|
||||
@Test public void testOutputDirShouldNotEffectImports() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : B {System.out.println(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
String subdir = tmpdir + "/sub";
|
||||
mkdir(subdir);
|
||||
writeFile(subdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
String outdir = tmpdir + "/out";
|
||||
mkdir(outdir);
|
||||
ErrorQueue equeue = antlr("M.g4", false, "-o", outdir, "-lib", subdir);
|
||||
assertEquals(0, equeue.size());
|
||||
}
|
||||
|
||||
@Test public void testTokensFileInOutputDirAndImportFileInSubdir() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : B {System.out.println(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
String subdir = tmpdir + "/sub";
|
||||
mkdir(subdir);
|
||||
writeFile(subdir, "S.g4", slave);
|
||||
String parser =
|
||||
"parser grammar MParser;\n" +
|
||||
"import S;\n" +
|
||||
"options {tokenVocab=MLexer;}\n" +
|
||||
"s : a ;\n";
|
||||
writeFile(tmpdir, "MParser.g4", parser);
|
||||
String lexer =
|
||||
"lexer grammar MLexer;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
writeFile(tmpdir, "MLexer.g4", lexer);
|
||||
String outdir = tmpdir + "/out";
|
||||
mkdir(outdir);
|
||||
ErrorQueue equeue = antlr("MLexer.g4", false, "-o", outdir);
|
||||
assertEquals(0, equeue.size());
|
||||
equeue = antlr("MParser.g4", false, "-o", outdir, "-lib", subdir);
|
||||
assertEquals(0, equeue.size());
|
||||
}
|
||||
|
||||
@Test public void testDelegatorInvokesDelegateRule() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : B {System.out.println(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "b", debug);
|
||||
assertEquals("S.a\n", found);
|
||||
}
|
||||
|
||||
@Test public void testBringInLiteralsFromDelegate() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : '=' 'a' {System.out.println(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "=a", debug);
|
||||
assertEquals("S.a\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatorInvokesDelegateRuleWithArgs() throws Exception {
|
||||
// must generate something like:
|
||||
// public int a(int x) throws RecognitionException { return gS.a(x); }
|
||||
// in M.
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a[int x] returns [int y] : B {System.out.print(\"S.a\"); $y=1000;} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : label=a[3] {System.out.println($label.y);} ;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "b", debug);
|
||||
assertEquals("S.a1000\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatorInvokesDelegateRuleWithReturnStruct() throws Exception {
|
||||
// must generate something like:
|
||||
// public int a(int x) throws RecognitionException { return gS.a(x); }
|
||||
// in M.
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : B {System.out.print(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a {System.out.println($a.text);} ;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "b", debug);
|
||||
assertEquals("S.ab\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatorAccessesDelegateMembers() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"@parser::members {\n" +
|
||||
" public void foo() {System.out.println(\"foo\");}\n" +
|
||||
"}\n" +
|
||||
"a : B ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" + // uses no rules from the import
|
||||
"import S;\n" +
|
||||
"s : 'b' {foo();} ;\n" + // gS is import pointer
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "b", debug);
|
||||
assertEquals("foo\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatorInvokesFirstVersionOfDelegateRule() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : b {System.out.println(\"S.a\");} ;\n" +
|
||||
"b : B ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String slave2 =
|
||||
"parser grammar T;\n" +
|
||||
"a : B {System.out.println(\"T.a\");} ;\n"; // hidden by S.a
|
||||
writeFile(tmpdir, "T.g4", slave2);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S,T;\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "b", debug);
|
||||
assertEquals("S.a\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatesSeeSameTokenType() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" + // A, B, C token type order
|
||||
"tokens { A, B, C }\n" +
|
||||
"x : A {System.out.println(\"S.x\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String slave2 =
|
||||
"parser grammar T;\n" +
|
||||
"tokens { C, B, A }\n" + // reverse order
|
||||
"y : A {System.out.println(\"T.y\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "T.g4", slave2);
|
||||
// The lexer will create rules to match letters a, b, c.
|
||||
// The associated token types A, B, C must have the same value
|
||||
// and all import'd parsers. Since ANTLR regenerates all imports
|
||||
// for use with the delegator M, it can generate the same token type
|
||||
// mapping in each parser:
|
||||
// public static final int C=6;
|
||||
// public static final int EOF=-1;
|
||||
// public static final int B=5;
|
||||
// public static final int WS=7;
|
||||
// public static final int A=4;
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S,T;\n" +
|
||||
"s : x y ;\n" + // matches AA, which should be "aa"
|
||||
"B : 'b' ;\n" + // another order: B, A, C
|
||||
"A : 'a' ;\n" +
|
||||
"C : 'c' ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "aa", debug);
|
||||
assertEquals("S.x\n" +
|
||||
"T.y\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatesSeeSameTokenType2() throws Exception {
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"parser grammar S;\n" + // A, B, C token type order
|
||||
"tokens { A, B, C }\n" +
|
||||
"x : A {System.out.println(\"S.x\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String slave2 =
|
||||
"parser grammar T;\n" +
|
||||
"tokens { C, B, A }\n" + // reverse order
|
||||
"y : A {System.out.println(\"T.y\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "T.g4", slave2);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S,T;\n" +
|
||||
"s : x y ;\n" + // matches AA, which should be "aa"
|
||||
"B : 'b' ;\n" + // another order: B, A, C
|
||||
"A : 'a' ;\n" +
|
||||
"C : 'c' ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
Grammar g = new Grammar(tmpdir+"/M.g4", master, equeue);
|
||||
|
||||
String expectedTokenIDToTypeMap = "{EOF=-1, B=1, A=2, C=3, WS=4}";
|
||||
String expectedStringLiteralToTypeMap = "{'a'=2, 'b'=1, 'c'=3}";
|
||||
String expectedTypeToTokenList = "[B, A, C, WS]";
|
||||
|
||||
assertEquals(expectedTokenIDToTypeMap, g.tokenNameToTypeMap.toString());
|
||||
assertEquals(expectedStringLiteralToTypeMap, sort(g.stringLiteralToTypeMap).toString());
|
||||
assertEquals(expectedTypeToTokenList, realElements(g.typeToTokenList).toString());
|
||||
|
||||
assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
|
||||
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "aa", debug);
|
||||
assertEquals("S.x\n" +
|
||||
"T.y\n", found);
|
||||
}
|
||||
|
||||
@Test public void testCombinedImportsCombined() throws Exception {
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"grammar S;\n" + // A, B, C token type order
|
||||
"tokens { A, B, C }\n" +
|
||||
"x : 'x' INT {System.out.println(\"S.x\");} ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : x INT ;\n";
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
Grammar g = new Grammar(tmpdir+"/M.g4", master, equeue);
|
||||
|
||||
assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
|
||||
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "x 34 9", debug);
|
||||
assertEquals("S.x\n", found);
|
||||
}
|
||||
|
||||
@Test public void testImportedTokenVocabIgnoredWithWarning() throws Exception {
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"options {tokenVocab=whatever;}\n" +
|
||||
"tokens { A }\n" +
|
||||
"x : A {System.out.println(\"S.x\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : x ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
Grammar g = new Grammar(tmpdir+"/M.g4", master, equeue);
|
||||
|
||||
Object expectedArg = "S";
|
||||
ErrorType expectedMsgID = ErrorType.OPTIONS_IN_DELEGATE;
|
||||
GrammarSemanticsMessage expectedMessage =
|
||||
new GrammarSemanticsMessage(expectedMsgID, g.fileName, null, expectedArg);
|
||||
checkGrammarSemanticsWarning(equeue, expectedMessage);
|
||||
|
||||
assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
|
||||
assertEquals("unexpected warnings: "+equeue, 1, equeue.warnings.size());
|
||||
}
|
||||
|
||||
@Test public void testSyntaxErrorsInImportsNotThrownOut() throws Exception {
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"options {toke\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : x ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
Grammar g = new Grammar(tmpdir+"/M.g4", master, equeue);
|
||||
|
||||
assertEquals(ErrorType.SYNTAX_ERROR, equeue.errors.get(0).getErrorType());
|
||||
}
|
||||
|
||||
@Test public void testDelegatorRuleOverridesDelegate() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : b {System.out.println(\"S.a\");} ;\n" +
|
||||
"b : B ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"b : 'b'|'c' ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"a", "c", debug);
|
||||
assertEquals("S.a\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatorRuleOverridesLookaheadInDelegate() throws Exception {
|
||||
String slave =
|
||||
"parser grammar JavaDecl;\n" +
|
||||
"type : 'int' ;\n" +
|
||||
"decl : type ID ';'\n" +
|
||||
" | type ID init ';' {System.out.println(\"JavaDecl: \"+$text);}\n" +
|
||||
" ;\n" +
|
||||
"init : '=' INT ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "JavaDecl.g4", slave);
|
||||
String master =
|
||||
"grammar Java;\n" +
|
||||
"import JavaDecl;\n" +
|
||||
"prog : decl ;\n" +
|
||||
"type : 'int' | 'float' ;\n" +
|
||||
"\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
// for float to work in decl, type must be overridden
|
||||
String found = execParser("Java.g4", master, "JavaParser", "JavaLexer",
|
||||
"prog", "float x = 3;", debug);
|
||||
assertEquals("JavaDecl: floatx=3;\n", found);
|
||||
}
|
||||
|
||||
@Test public void testDelegatorRuleOverridesDelegates() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : b {System.out.println(\"S.a\");} ;\n" +
|
||||
"b : 'b' ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
|
||||
String slave2 =
|
||||
"parser grammar T;\n" +
|
||||
"tokens { A }\n" +
|
||||
"b : 'b' {System.out.println(\"T.b\");} ;\n";
|
||||
writeFile(tmpdir, "T.g4", slave2);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S, T;\n" +
|
||||
"b : 'b'|'c' {System.out.println(\"M.b\");}|B|A ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"a", "c", debug);
|
||||
assertEquals("M.b\n" +
|
||||
"S.a\n", found);
|
||||
}
|
||||
// LEXER INHERITANCE
|
||||
|
||||
@Test public void testLexerDelegatorInvokesDelegateRule() throws Exception {
|
||||
String slave =
|
||||
"lexer grammar S;\n" +
|
||||
"A : 'a' {System.out.println(\"S.A\");} ;\n" +
|
||||
"C : 'c' ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"lexer grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"B : 'b' ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String expecting =
|
||||
"S.A\n" +
|
||||
"[@0,0:0='a',<3>,1:0]\n" +
|
||||
"[@1,1:1='b',<1>,1:1]\n" +
|
||||
"[@2,2:2='c',<4>,1:2]\n" +
|
||||
"[@3,3:2='<EOF>',<-1>,1:3]\n";
|
||||
String found = execLexer("M.g4", master, "M", "abc", debug);
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testLexerDelegatorRuleOverridesDelegate() throws Exception {
|
||||
String slave =
|
||||
"lexer grammar S;\n" +
|
||||
"A : 'a' {System.out.println(\"S.A\");} ;\n" +
|
||||
"B : 'b' {System.out.println(\"S.B\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"lexer grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"A : 'a' B {System.out.println(\"M.A\");} ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execLexer("M.g4", master, "M", "ab", debug);
|
||||
assertEquals("M.A\n" +
|
||||
"[@0,0:1='ab',<1>,1:0]\n" +
|
||||
"[@1,2:1='<EOF>',<-1>,1:2]\n", found);
|
||||
}
|
||||
|
||||
@Test public void testKeywordVSIDOrder() throws Exception {
|
||||
// rules in lexer are imported at END so rules in master override
|
||||
// *and* get priority over imported rules. So importing ID doesn't
|
||||
// mess up keywords in master grammar
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"lexer grammar S;\n" +
|
||||
"ID : 'a'..'z'+ ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"a : A {System.out.println(\"M.a: \"+$A);} ;\n" +
|
||||
"A : 'abc' {System.out.println(\"M.A\");} ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"a", "abc", debug);
|
||||
|
||||
assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
|
||||
assertEquals("unexpected warnings: "+equeue, 0, equeue.warnings.size());
|
||||
|
||||
assertEquals("M.A\n" +
|
||||
"M.a: [@0,0:2='abc',<1>,1:0]\n", found);
|
||||
}
|
||||
|
||||
// Make sure that M can import S that imports T.
|
||||
@Test public void test3LevelImport() throws Exception {
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"parser grammar T;\n" +
|
||||
"a : T ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "T.g4", slave);
|
||||
String slave2 =
|
||||
"parser grammar S;\n" +
|
||||
"import T;\n" +
|
||||
"a : S ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave2);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"a : M ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
Grammar g = new Grammar(tmpdir+"/M.g4", master, equeue);
|
||||
|
||||
String expectedTokenIDToTypeMap = "{EOF=-1, M=1}"; // S and T aren't imported; overridden
|
||||
String expectedStringLiteralToTypeMap = "{}";
|
||||
String expectedTypeToTokenList = "[M]";
|
||||
|
||||
assertEquals(expectedTokenIDToTypeMap,
|
||||
g.tokenNameToTypeMap.toString());
|
||||
assertEquals(expectedStringLiteralToTypeMap, g.stringLiteralToTypeMap.toString());
|
||||
assertEquals(expectedTypeToTokenList,
|
||||
realElements(g.typeToTokenList).toString());
|
||||
|
||||
assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
|
||||
|
||||
boolean ok =
|
||||
rawGenerateAndBuildRecognizer("M.g4", master, "MParser", null);
|
||||
boolean expecting = true; // should be ok
|
||||
assertEquals(expecting, ok);
|
||||
}
|
||||
|
||||
@Test public void testBigTreeOfImports() throws Exception {
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"parser grammar T;\n" +
|
||||
"tokens{T}\n" +
|
||||
"x : T ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "T.g4", slave);
|
||||
slave =
|
||||
"parser grammar S;\n" +
|
||||
"import T;\n" +
|
||||
"tokens{S}\n" +
|
||||
"y : S ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
|
||||
slave =
|
||||
"parser grammar C;\n" +
|
||||
"tokens{C}\n" +
|
||||
"i : C ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "C.g4", slave);
|
||||
slave =
|
||||
"parser grammar B;\n" +
|
||||
"tokens{B}\n" +
|
||||
"j : B ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "B.g4", slave);
|
||||
slave =
|
||||
"parser grammar A;\n" +
|
||||
"import B,C;\n" +
|
||||
"tokens{A}\n" +
|
||||
"k : A ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "A.g4", slave);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S,A;\n" +
|
||||
"tokens{M}\n" +
|
||||
"a : M ;\n" ;
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
Grammar g = new Grammar(tmpdir+"/M.g4", master, equeue);
|
||||
|
||||
assertEquals("[]", equeue.errors.toString());
|
||||
assertEquals("[]", equeue.warnings.toString());
|
||||
String expectedTokenIDToTypeMap = "{EOF=-1, M=1, S=2, T=3, A=4, B=5, C=6}";
|
||||
String expectedStringLiteralToTypeMap = "{}";
|
||||
String expectedTypeToTokenList = "[M, S, T, A, B, C]";
|
||||
|
||||
assertEquals(expectedTokenIDToTypeMap,
|
||||
g.tokenNameToTypeMap.toString());
|
||||
assertEquals(expectedStringLiteralToTypeMap, g.stringLiteralToTypeMap.toString());
|
||||
assertEquals(expectedTypeToTokenList,
|
||||
realElements(g.typeToTokenList).toString());
|
||||
|
||||
boolean ok =
|
||||
rawGenerateAndBuildRecognizer("M.g4", master, "MParser", null);
|
||||
boolean expecting = true; // should be ok
|
||||
assertEquals(expecting, ok);
|
||||
}
|
||||
|
||||
@Test public void testRulesVisibleThroughMultilevelImport() throws Exception {
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String slave =
|
||||
"parser grammar T;\n" +
|
||||
"x : T ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "T.g4", slave);
|
||||
String slave2 =
|
||||
"parser grammar S;\n" + // A, B, C token type order
|
||||
"import T;\n" +
|
||||
"a : S ;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave2);
|
||||
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"a : M x ;\n" ; // x MUST BE VISIBLE TO M
|
||||
writeFile(tmpdir, "M.g4", master);
|
||||
Grammar g = new Grammar(tmpdir+"/M.g4", master, equeue);
|
||||
|
||||
String expectedTokenIDToTypeMap = "{EOF=-1, M=1, T=2}";
|
||||
String expectedStringLiteralToTypeMap = "{}";
|
||||
String expectedTypeToTokenList = "[M, T]";
|
||||
|
||||
assertEquals(expectedTokenIDToTypeMap,
|
||||
g.tokenNameToTypeMap.toString());
|
||||
assertEquals(expectedStringLiteralToTypeMap, g.stringLiteralToTypeMap.toString());
|
||||
assertEquals(expectedTypeToTokenList,
|
||||
realElements(g.typeToTokenList).toString());
|
||||
|
||||
assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
|
||||
}
|
||||
|
||||
@Test public void testNestedComposite() throws Exception {
|
||||
// Wasn't compiling. http://www.antlr.org/jira/browse/ANTLR-438
|
||||
ErrorQueue equeue = new ErrorQueue();
|
||||
String gstr =
|
||||
"lexer grammar L;\n" +
|
||||
"T1: '1';\n" +
|
||||
"T2: '2';\n" +
|
||||
"T3: '3';\n" +
|
||||
"T4: '4';\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "L.g4", gstr);
|
||||
gstr =
|
||||
"parser grammar G1;\n" +
|
||||
"s: a | b;\n" +
|
||||
"a: T1;\n" +
|
||||
"b: T2;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "G1.g4", gstr);
|
||||
|
||||
gstr =
|
||||
"parser grammar G2;\n" +
|
||||
"import G1;\n" +
|
||||
"a: T3;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "G2.g4", gstr);
|
||||
String G3str =
|
||||
"grammar G3;\n" +
|
||||
"import G2;\n" +
|
||||
"b: T4;\n" ;
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "G3.g4", G3str);
|
||||
|
||||
Grammar g = new Grammar(tmpdir+"/G3.g4", G3str, equeue);
|
||||
|
||||
String expectedTokenIDToTypeMap = "{EOF=-1, T4=1, T3=2}";
|
||||
String expectedStringLiteralToTypeMap = "{}";
|
||||
String expectedTypeToTokenList = "[T4, T3]";
|
||||
|
||||
assertEquals(expectedTokenIDToTypeMap,
|
||||
g.tokenNameToTypeMap.toString());
|
||||
assertEquals(expectedStringLiteralToTypeMap, g.stringLiteralToTypeMap.toString());
|
||||
assertEquals(expectedTypeToTokenList,
|
||||
realElements(g.typeToTokenList).toString());
|
||||
|
||||
assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size());
|
||||
|
||||
boolean ok =
|
||||
rawGenerateAndBuildRecognizer("G3.g4", G3str, "G3Parser", null);
|
||||
boolean expecting = true; // should be ok
|
||||
assertEquals(expecting, ok);
|
||||
}
|
||||
|
||||
@Test public void testHeadersPropogatedCorrectlyToImportedGrammars() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a : B {System.out.print(\"S.a\");} ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"@header{package mypackage;}\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;" + // defines B from inherited token space
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
ErrorQueue equeue = antlr("M.g4", master, false);
|
||||
int expecting = 0; // should be ok
|
||||
assertEquals(expecting, equeue.errors.size());
|
||||
}
|
||||
|
||||
@Test public void testImportedRuleWithAction() throws Exception {
|
||||
// wasn't terminating. @after was injected into M as if it were @members
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"a @after {int x;} : B ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "b", debug);
|
||||
assertEquals("", found);
|
||||
}
|
||||
|
||||
@Test public void testImportedGrammarWithEmptyOptions() throws Exception {
|
||||
String slave =
|
||||
"parser grammar S;\n" +
|
||||
"options {}\n" +
|
||||
"a : B ;\n";
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "S.g4", slave);
|
||||
String master =
|
||||
"grammar M;\n" +
|
||||
"import S;\n" +
|
||||
"s : a ;\n" +
|
||||
"B : 'b' ;" +
|
||||
"WS : (' '|'\\n') -> skip ;\n" ;
|
||||
String found = execParser("M.g4", master, "MParser", "MLexer",
|
||||
"s", "b", debug);
|
||||
assertEquals("", found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#248 "Including grammar with
|
||||
* only fragments breaks generated lexer".
|
||||
* https://github.com/antlr/antlr4/issues/248
|
||||
*/
|
||||
@Test public void testImportLexerWithOnlyFragmentRules() {
|
||||
String slave =
|
||||
"lexer grammar Unicode;\n" +
|
||||
"\n" +
|
||||
"fragment\n" +
|
||||
"UNICODE_CLASS_Zs : '\\u0020' | '\\u00A0' | '\\u1680' | '\\u180E'\n" +
|
||||
" | '\\u2000'..'\\u200A'\n" +
|
||||
" | '\\u202F' | '\\u205F' | '\\u3000'\n" +
|
||||
" ;\n";
|
||||
String master =
|
||||
"grammar Test;\n" +
|
||||
"import Unicode;\n" +
|
||||
"\n" +
|
||||
"program : 'test' 'test' ;\n" +
|
||||
"\n" +
|
||||
"WS : (UNICODE_CLASS_Zs)+ -> skip;\n";
|
||||
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "Unicode.g4", slave);
|
||||
String found = execParser("Test.g4", master, "TestParser", "TestLexer", "program", "test test", debug);
|
||||
assertEquals("", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#670 "exception when importing
|
||||
* grammar".
|
||||
* https://github.com/antlr/antlr4/issues/670
|
||||
*/
|
||||
@Test
|
||||
public void testImportLargeGrammar() throws Exception {
|
||||
String slave = load("Java.g4", "UTF-8");
|
||||
String master =
|
||||
"grammar NewJava;\n" +
|
||||
"import Java;\n";
|
||||
|
||||
System.out.println("dir "+tmpdir);
|
||||
mkdir(tmpdir);
|
||||
writeFile(tmpdir, "Java.g4", slave);
|
||||
String found = execParser("NewJava.g4", master, "NewJavaParser", "NewJavaLexer", "compilationUnit", "package Foo;", debug);
|
||||
assertEquals("", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
}
|
|
@ -1,356 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/*
|
||||
cover these cases:
|
||||
dead end
|
||||
single alt
|
||||
single alt + preds
|
||||
conflict
|
||||
conflict + preds
|
||||
|
||||
*/
|
||||
public class TestFullContextParsing extends BaseTest {
|
||||
@Test public void testAmbigYieldsCtxSensitiveDFA() {
|
||||
String grammar =
|
||||
"grammar T;\n"+
|
||||
"s" +
|
||||
"@after {dumpDFA();}\n" +
|
||||
" : ID | ID {;} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"WS : (' '|'\\t'|'\\n')+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"abc", true);
|
||||
String expecting =
|
||||
"Decision 0:\n" +
|
||||
"s0-ID->:s1^=>1\n"; // ctx sensitive
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:0 reportAttemptingFullContext d=0 (s), input='abc'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
public String testCtxSensitiveDFA(String input) {
|
||||
String grammar =
|
||||
"grammar T;\n"+
|
||||
"s @after {dumpDFA();}\n" +
|
||||
" : '$' a | '@' b ;\n" +
|
||||
"a : e ID ;\n" +
|
||||
"b : e INT ID ;\n" +
|
||||
"e : INT | ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"INT : '0'..'9'+ ;\n"+
|
||||
"WS : (' '|'\\t'|'\\n')+ -> skip ;\n";
|
||||
return execParser("T.g4", grammar, "TParser", "TLexer", "s", input, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCtxSensitiveDFA1() {
|
||||
String result = testCtxSensitiveDFA("$ 34 abc");
|
||||
String expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-INT->s1\n" +
|
||||
"s1-ID->:s2^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:5 reportAttemptingFullContext d=1 (e), input='34abc'\n" +
|
||||
"line 1:2 reportContextSensitivity d=1 (e), input='34'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCtxSensitiveDFA2() {
|
||||
String result = testCtxSensitiveDFA("@ 34 abc");
|
||||
String expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-INT->s1\n" +
|
||||
"s1-ID->:s2^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:5 reportAttemptingFullContext d=1 (e), input='34abc'\n" +
|
||||
"line 1:5 reportContextSensitivity d=1 (e), input='34abc'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testCtxSensitiveDFATwoDiffInput() {
|
||||
String grammar =
|
||||
"grammar T;\n"+
|
||||
"s @after {dumpDFA();}\n" +
|
||||
" : ('$' a | '@' b)+ ;\n" +
|
||||
"a : e ID ;\n" +
|
||||
"b : e INT ID ;\n" +
|
||||
"e : INT | ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"INT : '0'..'9'+ ;\n"+
|
||||
"WS : (' '|'\\t'|'\\n')+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"$ 34 abc @ 34 abc", true);
|
||||
String expecting =
|
||||
"Decision 2:\n" +
|
||||
"s0-INT->s1\n" +
|
||||
"s1-ID->:s2^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:5 reportAttemptingFullContext d=2 (e), input='34abc'\n" +
|
||||
"line 1:2 reportContextSensitivity d=2 (e), input='34'\n" +
|
||||
"line 1:14 reportAttemptingFullContext d=2 (e), input='34abc'\n" +
|
||||
"line 1:14 reportContextSensitivity d=2 (e), input='34abc'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSLLSeesEOFInLLGrammar() {
|
||||
String grammar =
|
||||
"grammar T;\n"+
|
||||
"s @after {dumpDFA();}\n" +
|
||||
" : a ;\n" +
|
||||
"a : e ID ;\n" +
|
||||
"b : e INT ID ;\n" +
|
||||
"e : INT | ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"INT : '0'..'9'+ ;\n"+
|
||||
"WS : (' '|'\\t'|'\\n')+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"34 abc", true);
|
||||
String expecting =
|
||||
"Decision 0:\n" +
|
||||
"s0-INT->s1\n" +
|
||||
"s1-ID->:s2^=>1\n"; // Must point at accept state
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:3 reportAttemptingFullContext d=0 (e), input='34abc'\n" +
|
||||
"line 1:0 reportContextSensitivity d=0 (e), input='34'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testFullContextIF_THEN_ELSEParse() {
|
||||
String grammar =
|
||||
"grammar T;\n"+
|
||||
"s" +
|
||||
"@init {_interp.setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);}\n" +
|
||||
"@after {dumpDFA();}\n" +
|
||||
" : '{' stat* '}'" +
|
||||
" ;\n" +
|
||||
"stat: 'if' ID 'then' stat ('else' ID)?\n" +
|
||||
" | 'return'\n" +
|
||||
" ;" +
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"WS : (' '|'\\t'|'\\n')+ -> skip ;\n";
|
||||
String input = "{ if x then return }";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
input, true);
|
||||
String expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-'}'->:s1=>2\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals(null, this.stderrDuringParse);
|
||||
|
||||
input = "{ if x then return else foo }";
|
||||
result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
input, true);
|
||||
expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-'else'->:s1^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
// Technically, this input sequence is not ambiguous because else
|
||||
// uniquely predicts going into the optional subrule. else cannot
|
||||
// be matched by exiting stat since that would only match '}' or
|
||||
// the start of a stat. But, we are using the theory that
|
||||
// SLL(1)=LL(1) and so we are avoiding full context parsing
|
||||
// by declaring all else clause parsing to be ambiguous.
|
||||
assertEquals("line 1:19 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 1:19 reportContextSensitivity d=1 (stat), input='else'\n",
|
||||
this.stderrDuringParse);
|
||||
|
||||
input =
|
||||
"{ if x then if y then return else foo }";
|
||||
result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
input, true);
|
||||
expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-'}'->:s2=>2\n" +
|
||||
"s0-'else'->:s1^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:29 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 1:38 reportAmbiguity d=1 (stat): ambigAlts={1, 2}, input='elsefoo}'\n",
|
||||
this.stderrDuringParse);
|
||||
|
||||
// should not be ambiguous because the second 'else bar' clearly
|
||||
// indicates that the first else should match to the innermost if.
|
||||
// LL_EXACT_AMBIG_DETECTION makes us keep going to resolve
|
||||
|
||||
input =
|
||||
"{ if x then if y then return else foo else bar }";
|
||||
result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
input, true);
|
||||
expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-'else'->:s1^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:29 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 1:38 reportContextSensitivity d=1 (stat), input='elsefooelse'\n" +
|
||||
"line 1:38 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 1:38 reportContextSensitivity d=1 (stat), input='else'\n",
|
||||
this.stderrDuringParse);
|
||||
|
||||
input =
|
||||
"{ if x then return else foo\n" +
|
||||
"if x then if y then return else foo }";
|
||||
result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
input, true);
|
||||
expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-'}'->:s2=>2\n" +
|
||||
"s0-'else'->:s1^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:19 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 1:19 reportContextSensitivity d=1 (stat), input='else'\n" +
|
||||
"line 2:27 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 2:36 reportAmbiguity d=1 (stat): ambigAlts={1, 2}, input='elsefoo}'\n",
|
||||
this.stderrDuringParse);
|
||||
|
||||
input =
|
||||
"{ if x then return else foo\n" +
|
||||
"if x then if y then return else foo }";
|
||||
result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
input, true);
|
||||
expecting =
|
||||
"Decision 1:\n" +
|
||||
"s0-'}'->:s2=>2\n" +
|
||||
"s0-'else'->:s1^=>1\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:19 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 1:19 reportContextSensitivity d=1 (stat), input='else'\n" +
|
||||
"line 2:27 reportAttemptingFullContext d=1 (stat), input='else'\n" +
|
||||
"line 2:36 reportAmbiguity d=1 (stat): ambigAlts={1, 2}, input='elsefoo}'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests predictions for the following case involving closures.
|
||||
* http://www.antlr.org/wiki/display/~admin/2011/12/29/Flaw+in+ANTLR+v3+LL(*)+analysis+algorithm
|
||||
*/
|
||||
@Test
|
||||
public void testLoopsSimulateTailRecursion() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"prog\n" +
|
||||
"@init {_interp.setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);}\n" +
|
||||
" : expr_or_assign*;\n" +
|
||||
"expr_or_assign\n" +
|
||||
" : expr '++' {System.out.println(\"fail.\");}\n" +
|
||||
" | expr {System.out.println(\"pass: \"+$expr.text);}\n" +
|
||||
" ;\n" +
|
||||
"expr: expr_primary ('<-' ID)? ;\n" +
|
||||
"expr_primary\n" +
|
||||
" : '(' ID ')'\n" +
|
||||
" | ID '(' ID ')'\n" +
|
||||
" | ID\n" +
|
||||
" ;\n" +
|
||||
"ID : [a-z]+ ;\n" +
|
||||
"";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "prog", "a(i)<-x", true);
|
||||
assertEquals("pass: a(i)<-x\n", found);
|
||||
|
||||
String expecting =
|
||||
"line 1:3 reportAttemptingFullContext d=3 (expr_primary), input='a(i)'\n" +
|
||||
"line 1:7 reportAmbiguity d=3 (expr_primary): ambigAlts={2, 3}, input='a(i)<-x'\n";
|
||||
assertEquals(expecting, this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAmbiguityNoLoop() throws Exception {
|
||||
// simpler version of testLoopsSimulateTailRecursion, no loops
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"prog\n" +
|
||||
"@init {_interp.setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);}\n" +
|
||||
" : expr expr {System.out.println(\"alt 1\");}\n" +
|
||||
" | expr\n" +
|
||||
" ;\n" +
|
||||
"expr: '@'\n" +
|
||||
" | ID '@'\n" +
|
||||
" | ID\n" +
|
||||
" ;\n" +
|
||||
"ID : [a-z]+ ;\n" +
|
||||
"WS : [ \\r\\n\\t]+ -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "prog", "a@", true);
|
||||
assertEquals("alt 1\n", found);
|
||||
|
||||
String expecting =
|
||||
"line 1:2 reportAttemptingFullContext d=0 (prog), input='a@'\n" +
|
||||
"line 1:2 reportAmbiguity d=0 (prog): ambigAlts={1, 2}, input='a@'\n" +
|
||||
"line 1:2 reportAttemptingFullContext d=1 (expr), input='a@'\n" +
|
||||
"line 1:2 reportContextSensitivity d=1 (expr), input='a@'\n";
|
||||
assertEquals(expecting, this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExprAmbiguity() throws Exception {
|
||||
// translated left-recursive expr rule to test ambig detection
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {_interp.setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);}\n" +
|
||||
" : expr[0] {System.out.println($expr.ctx.toStringTree(this));} ;\n" +
|
||||
"\n" +
|
||||
"expr[int _p]\n" +
|
||||
" : ID\n" +
|
||||
" ( {5 >= $_p}? '*' expr[6]\n" +
|
||||
" | {4 >= $_p}? '+' expr[5]\n" +
|
||||
" )*\n" +
|
||||
" ;\n" +
|
||||
"\n" +
|
||||
"ID : [a-zA-Z]+ ; // match identifiers\n" +
|
||||
"WS : [ \\t\\r\\n]+ -> skip ; // toss out whitespace\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s", "a+b", true);
|
||||
assertEquals("(expr a + (expr b))\n", found);
|
||||
|
||||
String expecting =
|
||||
"line 1:1 reportAttemptingFullContext d=1 (expr), input='+'\n" +
|
||||
"line 1:2 reportContextSensitivity d=1 (expr), input='+b'\n";
|
||||
assertEquals(expecting, this.stderrDuringParse);
|
||||
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s", "a+b*c", true);
|
||||
assertEquals("(expr a + (expr b * (expr c)))\n", found);
|
||||
|
||||
expecting =
|
||||
"line 1:1 reportAttemptingFullContext d=1 (expr), input='+'\n" +
|
||||
"line 1:2 reportContextSensitivity d=1 (expr), input='+b'\n" +
|
||||
"line 1:3 reportAttemptingFullContext d=1 (expr), input='*'\n" +
|
||||
"line 1:5 reportAmbiguity d=1 (expr): ambigAlts={1, 2}, input='*c'\n";
|
||||
assertEquals(expecting, this.stderrDuringParse);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,732 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.antlr.v4.tool.ErrorType;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/** */
|
||||
public class TestLeftRecursion extends BaseTest {
|
||||
protected boolean debug = false;
|
||||
|
||||
@Test public void testSimple() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : a ;\n" +
|
||||
"a : a ID\n" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"s", "x", debug);
|
||||
String expecting = "(s (a x))\n";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"s", "x y", debug);
|
||||
expecting = "(s (a (a x) y))\n";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"s", "x y z", debug);
|
||||
expecting = "(s (a (a (a x) y) z))\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for "Support direct calls to left-recursive
|
||||
* rules".
|
||||
* https://github.com/antlr/antlr4/issues/161
|
||||
*/
|
||||
@Test public void testDirectCallToLeftRecursiveRule() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a @after {System.out.println($ctx.toStringTree(this));} : a ID\n" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x", debug);
|
||||
String expecting = "(a x)\n";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x y", debug);
|
||||
expecting = "(a (a x) y)\n";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x y z", debug);
|
||||
expecting = "(a (a (a x) y) z)\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testSemPred() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : a ;\n" +
|
||||
"a : a {true}? ID\n" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"s", "x y z", debug);
|
||||
String expecting = "(s (a (a (a x) y) z))\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSemPredFailOption() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : a ;\n" +
|
||||
"a : a ID {false}?<fail='custom message'>\n" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"s", "x y z", debug);
|
||||
String expecting = "(s (a (a x) y z))\n";
|
||||
assertEquals(expecting, found);
|
||||
assertEquals("line 1:4 rule a custom message\n", stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testTernaryExpr() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : e EOF ;\n" + // must indicate EOF can follow or 'a<EOF>' won't match
|
||||
"e : e '*' e" +
|
||||
" | e '+' e" +
|
||||
" |<assoc=right> e '?' e ':' e" +
|
||||
" |<assoc=right> e '=' e" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a", "(s (e a) <EOF>)",
|
||||
"a+b", "(s (e (e a) + (e b)) <EOF>)",
|
||||
"a*b", "(s (e (e a) * (e b)) <EOF>)",
|
||||
"a?b:c", "(s (e (e a) ? (e b) : (e c)) <EOF>)",
|
||||
"a=b=c", "(s (e (e a) = (e (e b) = (e c))) <EOF>)",
|
||||
"a?b+c:d", "(s (e (e a) ? (e (e b) + (e c)) : (e d)) <EOF>)",
|
||||
"a?b=c:d", "(s (e (e a) ? (e (e b) = (e c)) : (e d)) <EOF>)",
|
||||
"a? b?c:d : e", "(s (e (e a) ? (e (e b) ? (e c) : (e d)) : (e e)) <EOF>)",
|
||||
"a?b: c?d:e", "(s (e (e a) ? (e b) : (e (e c) ? (e d) : (e e))) <EOF>)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#542 "First alternative cannot
|
||||
* be right-associative".
|
||||
* https://github.com/antlr/antlr4/issues/542
|
||||
*/
|
||||
@Test public void testTernaryExprExplicitAssociativity() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : e EOF ;\n" + // must indicate EOF can follow or 'a<EOF>' won't match
|
||||
"e :<assoc=right> e '*' e" +
|
||||
" |<assoc=right> e '+' e" +
|
||||
" |<assoc=right> e '?' e ':' e" +
|
||||
" |<assoc=right> e '=' e" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a", "(s (e a) <EOF>)",
|
||||
"a+b", "(s (e (e a) + (e b)) <EOF>)",
|
||||
"a*b", "(s (e (e a) * (e b)) <EOF>)",
|
||||
"a?b:c", "(s (e (e a) ? (e b) : (e c)) <EOF>)",
|
||||
"a=b=c", "(s (e (e a) = (e (e b) = (e c))) <EOF>)",
|
||||
"a?b+c:d", "(s (e (e a) ? (e (e b) + (e c)) : (e d)) <EOF>)",
|
||||
"a?b=c:d", "(s (e (e a) ? (e (e b) = (e c)) : (e d)) <EOF>)",
|
||||
"a? b?c:d : e", "(s (e (e a) ? (e (e b) ? (e c) : (e d)) : (e e)) <EOF>)",
|
||||
"a?b: c?d:e", "(s (e (e a) ? (e b) : (e (e c) ? (e d) : (e e))) <EOF>)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test public void testExpressions() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : e EOF ;\n" + // must indicate EOF can follow
|
||||
"e : e '.' ID\n" +
|
||||
" | e '.' 'this'\n" +
|
||||
" | '-' e\n" +
|
||||
" | e '*' e\n" +
|
||||
" | e ('+'|'-') e\n" +
|
||||
" | INT\n" +
|
||||
" | ID\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a", "(s (e a) <EOF>)",
|
||||
"1", "(s (e 1) <EOF>)",
|
||||
"a-1", "(s (e (e a) - (e 1)) <EOF>)",
|
||||
"a.b", "(s (e (e a) . b) <EOF>)",
|
||||
"a.this", "(s (e (e a) . this) <EOF>)",
|
||||
"-a", "(s (e - (e a)) <EOF>)",
|
||||
"-a+b", "(s (e (e - (e a)) + (e b)) <EOF>)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test public void testJavaExpressions() throws Exception {
|
||||
// Generates about 7k in bytecodes for generated e_ rule;
|
||||
// Well within the 64k method limit. e_primary compiles
|
||||
// to about 2k in bytecodes.
|
||||
// this is simplified from real java
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : e EOF ;\n" + // must indicate EOF can follow
|
||||
"expressionList\n" +
|
||||
" : e (',' e)*\n" +
|
||||
" ;\n" +
|
||||
"e : '(' e ')'\n" +
|
||||
" | 'this' \n" +
|
||||
" | 'super'\n" +
|
||||
" | INT\n" +
|
||||
" | ID\n" +
|
||||
" | type '.' 'class'\n" +
|
||||
" | e '.' ID\n" +
|
||||
" | e '.' 'this'\n" +
|
||||
" | e '.' 'super' '(' expressionList? ')'\n" +
|
||||
" | e '.' 'new' ID '(' expressionList? ')'\n" +
|
||||
" | 'new' type ( '(' expressionList? ')' | ('[' e ']')+)\n" +
|
||||
" | e '[' e ']'\n" +
|
||||
" | '(' type ')' e\n" +
|
||||
" | e ('++' | '--')\n" +
|
||||
" | e '(' expressionList? ')'\n" +
|
||||
" | ('+'|'-'|'++'|'--') e\n" +
|
||||
" | ('~'|'!') e\n" +
|
||||
" | e ('*'|'/'|'%') e\n" +
|
||||
" | e ('+'|'-') e\n" +
|
||||
" | e ('<<' | '>>>' | '>>') e\n" +
|
||||
" | e ('<=' | '>=' | '>' | '<') e\n" +
|
||||
" | e 'instanceof' e\n" +
|
||||
" | e ('==' | '!=') e\n" +
|
||||
" | e '&' e\n" +
|
||||
" |<assoc=right> e '^' e\n" +
|
||||
" | e '|' e\n" +
|
||||
" | e '&&' e\n" +
|
||||
" | e '||' e\n" +
|
||||
" | e '?' e ':' e\n" +
|
||||
" |<assoc=right>" +
|
||||
" e ('='\n" +
|
||||
" |'+='\n" +
|
||||
" |'-='\n" +
|
||||
" |'*='\n" +
|
||||
" |'/='\n" +
|
||||
" |'&='\n" +
|
||||
" |'|='\n" +
|
||||
" |'^='\n" +
|
||||
" |'>>='\n" +
|
||||
" |'>>>='\n" +
|
||||
" |'<<='\n" +
|
||||
" |'%=') e\n" +
|
||||
" ;\n" +
|
||||
"type: ID \n" +
|
||||
" | ID '[' ']'\n" +
|
||||
" | 'int'\n" +
|
||||
" | 'int' '[' ']' \n" +
|
||||
" ;\n" +
|
||||
"ID : ('a'..'z'|'A'..'Z'|'_'|'$')+;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a|b&c", "(s (e (e a) | (e (e b) & (e c))) <EOF>)",
|
||||
"(a|b)&c", "(s (e (e ( (e (e a) | (e b)) )) & (e c)) <EOF>)",
|
||||
"a > b", "(s (e (e a) > (e b)) <EOF>)",
|
||||
"a >> b", "(s (e (e a) >> (e b)) <EOF>)",
|
||||
"a=b=c", "(s (e (e a) = (e (e b) = (e c))) <EOF>)",
|
||||
"a^b^c", "(s (e (e a) ^ (e (e b) ^ (e c))) <EOF>)",
|
||||
"(T)x", "(s (e ( (type T) ) (e x)) <EOF>)",
|
||||
"new A().b", "(s (e (e new (type A) ( )) . b) <EOF>)",
|
||||
"(T)t.f()", "(s (e (e ( (type T) ) (e (e t) . f)) ( )) <EOF>)",
|
||||
"a.f(x)==T.c", "(s (e (e (e (e a) . f) ( (expressionList (e x)) )) == (e (e T) . c)) <EOF>)",
|
||||
"a.f().g(x,1)", "(s (e (e (e (e (e a) . f) ( )) . g) ( (expressionList (e x) , (e 1)) )) <EOF>)",
|
||||
"new T[((n-1) * x) + 1]", "(s (e new (type T) [ (e (e ( (e (e ( (e (e n) - (e 1)) )) * (e x)) )) + (e 1)) ]) <EOF>)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test public void testDeclarations() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : declarator EOF ;\n" + // must indicate EOF can follow
|
||||
"declarator\n" +
|
||||
" : declarator '[' e ']'\n" +
|
||||
" | declarator '[' ']'\n" +
|
||||
" | declarator '(' ')'\n" +
|
||||
" | '*' declarator\n" + // binds less tight than suffixes
|
||||
" | '(' declarator ')'\n" +
|
||||
" | ID\n" +
|
||||
" ;\n" +
|
||||
"e : INT ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a", "(s (declarator a) <EOF>)",
|
||||
"*a", "(s (declarator * (declarator a)) <EOF>)",
|
||||
"**a", "(s (declarator * (declarator * (declarator a))) <EOF>)",
|
||||
"a[3]", "(s (declarator (declarator a) [ (e 3) ]) <EOF>)",
|
||||
"b[]", "(s (declarator (declarator b) [ ]) <EOF>)",
|
||||
"(a)", "(s (declarator ( (declarator a) )) <EOF>)",
|
||||
"a[]()", "(s (declarator (declarator (declarator a) [ ]) ( )) <EOF>)",
|
||||
"a[][]", "(s (declarator (declarator (declarator a) [ ]) [ ]) <EOF>)",
|
||||
"*a[]", "(s (declarator * (declarator (declarator a) [ ])) <EOF>)",
|
||||
"(*a)[]", "(s (declarator (declarator ( (declarator * (declarator a)) )) [ ]) <EOF>)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test public void testReturnValueAndActions() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : e {System.out.println($e.v);} ;\n" +
|
||||
"e returns [int v, List<String> ignored]\n" +
|
||||
" : a=e '*' b=e {$v = $a.v * $b.v;}\n" +
|
||||
" | a=e '+' b=e {$v = $a.v + $b.v;}\n" +
|
||||
" | INT {$v = $INT.int;}\n" +
|
||||
" | '(' x=e ')' {$v = $x.v;}\n" +
|
||||
" ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"4", "4",
|
||||
"1+2", "3",
|
||||
"1+2*3", "7",
|
||||
"(1+2)*3", "9",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#677 "labels not working in
|
||||
* grammar file".
|
||||
* https://github.com/antlr/antlr4/issues/677
|
||||
*
|
||||
* <p>This test treats {@code ,} and {@code >>} as part of a single compound
|
||||
* operator (similar to a ternary operator).</p>
|
||||
*/
|
||||
@Test public void testReturnValueAndActionsList1() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : expr EOF;\n" +
|
||||
"expr:\n" +
|
||||
" a=expr '*' a=expr #Factor\n" +
|
||||
" | b+=expr (',' b+=expr)* '>>' c=expr #Send\n" +
|
||||
" | ID #JustId //semantic check on modifiers\n" +
|
||||
";\n" +
|
||||
"\n" +
|
||||
"ID : ('a'..'z'|'A'..'Z'|'_')\n" +
|
||||
" ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*\n" +
|
||||
";\n" +
|
||||
"\n" +
|
||||
"WS : [ \\t\\n]+ -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a*b", "(s (expr (expr a) * (expr b)) <EOF>)",
|
||||
"a,c>>x", "(s (expr (expr a) , (expr c) >> (expr x)) <EOF>)",
|
||||
"x", "(s (expr x) <EOF>)",
|
||||
"a*b,c,x*y>>r", "(s (expr (expr (expr a) * (expr b)) , (expr c) , (expr (expr x) * (expr y)) >> (expr r)) <EOF>)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#677 "labels not working in
|
||||
* grammar file".
|
||||
* https://github.com/antlr/antlr4/issues/677
|
||||
*
|
||||
* <p>This test treats the {@code ,} and {@code >>} operators separately.</p>
|
||||
*/
|
||||
@Test public void testReturnValueAndActionsList2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : expr EOF;\n" +
|
||||
"expr:\n" +
|
||||
" a=expr '*' a=expr #Factor\n" +
|
||||
" | b+=expr ',' b+=expr #Comma\n" +
|
||||
" | b+=expr '>>' c=expr #Send\n" +
|
||||
" | ID #JustId //semantic check on modifiers\n" +
|
||||
";\n" +
|
||||
"\n" +
|
||||
"ID : ('a'..'z'|'A'..'Z'|'_')\n" +
|
||||
" ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*\n" +
|
||||
";\n" +
|
||||
"\n" +
|
||||
"WS : [ \\t\\n]+ -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a*b", "(s (expr (expr a) * (expr b)) <EOF>)",
|
||||
"a,c>>x", "(s (expr (expr (expr a) , (expr c)) >> (expr x)) <EOF>)",
|
||||
"x", "(s (expr x) <EOF>)",
|
||||
"a*b,c,x*y>>r", "(s (expr (expr (expr (expr (expr a) * (expr b)) , (expr c)) , (expr (expr x) * (expr y))) >> (expr r)) <EOF>)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test public void testLabelsOnOpSubrule() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : e ;\n" +
|
||||
"e : a=e op=('*'|'/') b=e {}\n" +
|
||||
" | INT {}\n" +
|
||||
" | '(' x=e ')' {}\n" +
|
||||
" ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"4", "(s (e 4))",
|
||||
"1*2/3", "(s (e (e (e 1) * (e 2)) / (e 3)))",
|
||||
"(1/2)*3", "(s (e (e ( (e (e 1) / (e 2)) )) * (e 3)))",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test public void testReturnValueAndActionsAndLabels() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : q=e {System.out.println($e.v);} ;\n" +
|
||||
"\n" +
|
||||
"e returns [int v]\n" +
|
||||
" : a=e op='*' b=e {$v = $a.v * $b.v;} # mult\n" +
|
||||
" | a=e '+' b=e {$v = $a.v + $b.v;} # add\n" +
|
||||
" | INT {$v = $INT.int;} # anInt\n" +
|
||||
" | '(' x=e ')' {$v = $x.v;} # parens\n" +
|
||||
" | x=e '++' {$v = $x.v+1;} # inc\n" +
|
||||
" | e '--' # dec\n" +
|
||||
" | ID {$v = 3;} # anID\n" +
|
||||
" ; \n" +
|
||||
"\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"4", "4",
|
||||
"1+2", "3",
|
||||
"1+2*3", "7",
|
||||
"i++*3", "12",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#433 "Not all context accessor
|
||||
* methods are generated when an alternative rule label is used for multiple
|
||||
* alternatives".
|
||||
* https://github.com/antlr/antlr4/issues/433
|
||||
*/
|
||||
@Test public void testMultipleAlternativesWithCommonLabel() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : e {System.out.println($e.v);} ;\n" +
|
||||
"\n" +
|
||||
"e returns [int v]\n" +
|
||||
" : e '*' e {$v = ((BinaryContext)$ctx).e(0).v * ((BinaryContext)$ctx).e(1).v;} # binary\n" +
|
||||
" | e '+' e {$v = ((BinaryContext)$ctx).e(0).v + ((BinaryContext)$ctx).e(1).v;} # binary\n" +
|
||||
" | INT {$v = $INT.int;} # anInt\n" +
|
||||
" | '(' e ')' {$v = $e.v;} # parens\n" +
|
||||
" | left=e INC {assert(((UnaryContext)$ctx).INC() != null); $v = $left.v + 1;} # unary\n" +
|
||||
" | left=e DEC {assert(((UnaryContext)$ctx).DEC() != null); $v = $left.v - 1;} # unary\n" +
|
||||
" | ID {$v = 3;} # anID\n" +
|
||||
" ; \n" +
|
||||
"\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"INC : '++' ;\n" +
|
||||
"DEC : '--' ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"4", "4",
|
||||
"1+2", "3",
|
||||
"1+2*3", "7",
|
||||
"i++*3", "12",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test public void testPrefixOpWithActionAndLabel() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : e {System.out.println($e.result);} ;\n" +
|
||||
"\n" +
|
||||
"e returns [String result]\n" +
|
||||
" : ID '=' e1=e { $result = \"(\" + $ID.getText() + \"=\" + $e1.result + \")\"; }\n" +
|
||||
" | ID { $result = $ID.getText(); }\n" +
|
||||
" | e1=e '+' e2=e { $result = \"(\" + $e1.result + \"+\" + $e2.result + \")\"; }\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"a", "a",
|
||||
"a+b", "(a+b)",
|
||||
"a=b+c", "((a=b)+c)",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAmbigLR() throws Exception {
|
||||
String grammar =
|
||||
"grammar Expr;\n" +
|
||||
"prog: stat ;\n" +
|
||||
"stat: expr NEWLINE # printExpr\n" +
|
||||
" | ID '=' expr NEWLINE # assign\n" +
|
||||
" | NEWLINE # blank\n" +
|
||||
" ;\n" +
|
||||
"expr: expr ('*'|'/') expr # MulDiv\n" +
|
||||
" | expr ('+'|'-') expr # AddSub\n" +
|
||||
" | INT # int\n" +
|
||||
" | ID # id\n" +
|
||||
" | '(' expr ')' # parens\n" +
|
||||
" ;\n" +
|
||||
"\n" +
|
||||
"MUL : '*' ; // assigns token name to '*' used above in grammar\n" +
|
||||
"DIV : '/' ;\n" +
|
||||
"ADD : '+' ;\n" +
|
||||
"SUB : '-' ;\n" +
|
||||
"ID : [a-zA-Z]+ ; // match identifiers\n" +
|
||||
"INT : [0-9]+ ; // match integers\n" +
|
||||
"NEWLINE:'\\r'? '\\n' ; // return newlines to parser (is end-statement signal)\n" +
|
||||
"WS : [ \\t]+ -> skip ; // toss out whitespace\n";
|
||||
String result = execParser("Expr.g4", grammar, "ExprParser", "ExprLexer", "prog", "1\n", true);
|
||||
assertNull(stderrDuringParse);
|
||||
|
||||
result = execParser("Expr.g4", grammar, "ExprParser", "ExprLexer", "prog", "a = 5\n", true);
|
||||
assertNull(stderrDuringParse);
|
||||
|
||||
result = execParser("Expr.g4", grammar, "ExprParser", "ExprLexer", "prog", "b = 6\n", true);
|
||||
assertNull(stderrDuringParse);
|
||||
|
||||
result = execParser("Expr.g4", grammar, "ExprParser", "ExprLexer", "prog", "a+b*2\n", true);
|
||||
assertNull(stderrDuringParse);
|
||||
|
||||
result = execParser("Expr.g4", grammar, "ExprParser", "ExprLexer", "prog", "(1+2)*3\n", true);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testCheckForNonLeftRecursiveRule() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : a ;\n" +
|
||||
"a : a ID\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String expected =
|
||||
"error(" + ErrorType.NO_NON_LR_ALTS.code + "): T.g4:3:0: left recursive rule a must contain an alternative which is not left recursive\n";
|
||||
testErrors(new String[] { grammar, expected }, false);
|
||||
}
|
||||
|
||||
@Test public void testCheckForLeftRecursiveEmptyFollow() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : a ;\n" +
|
||||
"a : a ID?\n" +
|
||||
" | ID\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String expected =
|
||||
"error(" + ErrorType.EPSILON_LR_FOLLOW.code + "): T.g4:3:0: left recursive rule a contains a left recursive alternative which can be followed by the empty string\n";
|
||||
testErrors(new String[] { grammar, expected }, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for #239 "recoursive parser using implicit
|
||||
* tokens ignore white space lexer rule".
|
||||
* https://github.com/antlr/antlr4/issues/239
|
||||
*/
|
||||
@Test public void testWhitespaceInfluence() {
|
||||
String grammar =
|
||||
"grammar Expr;\n" +
|
||||
"prog : expression EOF;\n" +
|
||||
"expression\n" +
|
||||
" : ID '(' expression (',' expression)* ')' # doFunction\n" +
|
||||
" | '(' expression ')' # doParenthesis\n" +
|
||||
" | '!' expression # doNot\n" +
|
||||
" | '-' expression # doNegate\n" +
|
||||
" | '+' expression # doPositiv\n" +
|
||||
" | expression '^' expression # doPower\n" +
|
||||
" | expression '*' expression # doMultipy\n" +
|
||||
" | expression '/' expression # doDivide\n" +
|
||||
" | expression '%' expression # doModulo\n" +
|
||||
" | expression '-' expression # doMinus\n" +
|
||||
" | expression '+' expression # doPlus\n" +
|
||||
" | expression '=' expression # doEqual\n" +
|
||||
" | expression '!=' expression # doNotEqual\n" +
|
||||
" | expression '>' expression # doGreather\n" +
|
||||
" | expression '>=' expression # doGreatherEqual\n" +
|
||||
" | expression '<' expression # doLesser\n" +
|
||||
" | expression '<=' expression # doLesserEqual\n" +
|
||||
" | expression K_IN '(' expression (',' expression)* ')' # doIn\n" +
|
||||
" | expression ( '&' | K_AND) expression # doAnd\n" +
|
||||
" | expression ( '|' | K_OR) expression # doOr\n" +
|
||||
" | '[' expression (',' expression)* ']' # newArray\n" +
|
||||
" | K_TRUE # newTrueBoolean\n" +
|
||||
" | K_FALSE # newFalseBoolean\n" +
|
||||
" | NUMBER # newNumber\n" +
|
||||
" | DATE # newDateTime\n" +
|
||||
" | ID # newIdentifier\n" +
|
||||
" | SQ_STRING # newString\n" +
|
||||
" | K_NULL # newNull\n" +
|
||||
" ;\n" +
|
||||
"\n" +
|
||||
"// Fragments\n" +
|
||||
"fragment DIGIT : '0' .. '9'; \n" +
|
||||
"fragment UPPER : 'A' .. 'Z';\n" +
|
||||
"fragment LOWER : 'a' .. 'z';\n" +
|
||||
"fragment LETTER : LOWER | UPPER;\n" +
|
||||
"fragment WORD : LETTER | '_' | '$' | '#' | '.';\n" +
|
||||
"fragment ALPHANUM : WORD | DIGIT; \n" +
|
||||
"\n" +
|
||||
"// Tokens\n" +
|
||||
"ID : LETTER ALPHANUM*;\n" +
|
||||
"NUMBER : DIGIT+ ('.' DIGIT+)? (('e'|'E')('+'|'-')? DIGIT+)?;\n" +
|
||||
"DATE : '\\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT (' ' DIGIT DIGIT ':' DIGIT DIGIT ':' DIGIT DIGIT ('.' DIGIT+)?)? '\\'';\n" +
|
||||
"SQ_STRING : '\\'' ('\\'\\'' | ~'\\'')* '\\'';\n" +
|
||||
"DQ_STRING : '\"' ('\\\\\"' | ~'\"')* '\"';\n" +
|
||||
"WS : [ \\t\\n\\r]+ -> skip ;\n" +
|
||||
"COMMENTS : ('/*' .*? '*/' | '//' ~'\\n'* '\\n' ) -> skip;\n";
|
||||
|
||||
String expected =
|
||||
"";
|
||||
String result = execParser("Expr.g4", grammar, "ExprParser", "ExprLexer", "prog", "Test(1,3)", false);
|
||||
assertEquals(expected, result);
|
||||
assertNull(stderrDuringParse);
|
||||
|
||||
expected =
|
||||
"";
|
||||
result = execParser("Expr.g4", grammar, "ExprParser", "ExprLexer", "prog", "Test(1, 3)", false);
|
||||
assertEquals(expected, result);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#509 "Incorrect rule chosen in
|
||||
* unambiguous grammar".
|
||||
* https://github.com/antlr/antlr4/issues/509
|
||||
*/
|
||||
@Test public void testPrecedenceFilterConsidersContext() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"prog\n" +
|
||||
"@after {System.out.println($ctx.toStringTree(this));}\n" +
|
||||
": statement* EOF {};\n" +
|
||||
"statement: letterA | statement letterA 'b' ;\n" +
|
||||
"letterA: 'a';\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "prog",
|
||||
"aa", false);
|
||||
assertEquals("(prog (statement (letterA a)) (statement (letterA a)) <EOF>)\n", found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#625 "Duplicate action breaks
|
||||
* operator precedence"
|
||||
* https://github.com/antlr/antlr4/issues/625
|
||||
*/
|
||||
@Test public void testMultipleActions() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : e ;\n" +
|
||||
"e : a=e op=('*'|'/') b=e {}{}\n" +
|
||||
" | INT {}{}\n" +
|
||||
" | '(' x=e ')' {}{}\n" +
|
||||
" ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"4", "(s (e 4))",
|
||||
"1*2/3", "(s (e (e (e 1) * (e 2)) / (e 3)))",
|
||||
"(1/2)*3", "(s (e (e ( (e (e 1) / (e 2)) )) * (e 3)))",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#625 "Duplicate action breaks
|
||||
* operator precedence"
|
||||
* https://github.com/antlr/antlr4/issues/625
|
||||
*/
|
||||
@Test public void testMultipleActionsPredicatesOptions() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s @after {System.out.println($ctx.toStringTree(this));} : e ;\n" +
|
||||
"e : a=e op=('*'|'/') b=e {}{true}?\n" +
|
||||
" | a=e op=('+'|'-') b=e {}<p=3>{true}?<fail='Message'>\n" +
|
||||
" | INT {}{}\n" +
|
||||
" | '(' x=e ')' {}{}\n" +
|
||||
" ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
String[] tests = {
|
||||
"4", "(s (e 4))",
|
||||
"1*2/3", "(s (e (e (e 1) * (e 2)) / (e 3)))",
|
||||
"(1/2)*3", "(s (e (e ( (e (e 1) / (e 2)) )) * (e 3)))",
|
||||
};
|
||||
runTests(grammar, tests, "s");
|
||||
}
|
||||
|
||||
public void runTests(String grammar, String[] tests, String startRule) {
|
||||
boolean success = rawGenerateAndBuildRecognizer("T.g4", grammar, "TParser", "TLexer");
|
||||
assertTrue(success);
|
||||
writeRecognizerAndCompile("TParser",
|
||||
"TLexer",
|
||||
startRule,
|
||||
debug,
|
||||
false);
|
||||
|
||||
for (int i=0; i<tests.length; i+=2) {
|
||||
String test = tests[i];
|
||||
String expecting = tests[i+1]+"\n";
|
||||
writeFile(tmpdir, "input", test);
|
||||
String found = execRecognizer();
|
||||
System.out.print(test+" -> "+found);
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestLexerErrors extends BaseTest {
|
||||
// TEST DETECTION
|
||||
@Test public void testInvalidCharAtStart() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"A : 'a' 'b' ;\n";
|
||||
String tokens = execLexer("L.g4", grammar, "L", "x");
|
||||
String expectingTokens =
|
||||
"[@0,1:0='<EOF>',<-1>,1:1]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
String expectingError = "line 1:0 token recognition error at: 'x'\n";
|
||||
String error = stderrDuringParse;
|
||||
assertEquals(expectingError, error);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringsEmbeddedInActions() {
|
||||
String grammar =
|
||||
"lexer grammar Actions;\n"
|
||||
+ "ACTION2 : '[' (STRING | ~'\"')*? ']';\n"
|
||||
+ "STRING : '\"' ('\\\"' | .)*? '\"';\n"
|
||||
+ "WS : [ \\t\\r\\n]+ -> skip;\n";
|
||||
String tokens = execLexer("Actions.g4", grammar, "Actions", "[\"foo\"]");
|
||||
String expectingTokens =
|
||||
"[@0,0:6='[\"foo\"]',<1>,1:0]\n" +
|
||||
"[@1,7:6='<EOF>',<-1>,1:7]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
assertNull(stderrDuringParse);
|
||||
|
||||
tokens = execLexer("Actions.g4", grammar, "Actions", "[\"foo]");
|
||||
expectingTokens =
|
||||
"[@0,6:5='<EOF>',<-1>,1:6]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
assertEquals("line 1:0 token recognition error at: '[\"foo]'\n", stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testEnforcedGreedyNestedBrances() {
|
||||
String grammar =
|
||||
"lexer grammar R;\n"
|
||||
+ "ACTION : '{' (ACTION | ~[{}])* '}';\n"
|
||||
+ "WS : [ \\r\\n\\t]+ -> skip;\n";
|
||||
String tokens = execLexer("R.g4", grammar, "R", "{ { } }");
|
||||
String expectingTokens =
|
||||
"[@0,0:6='{ { } }',<1>,1:0]\n" +
|
||||
"[@1,7:6='<EOF>',<-1>,1:7]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
assertEquals(null, stderrDuringParse);
|
||||
|
||||
tokens = execLexer("R.g4", grammar, "R", "{ { }");
|
||||
expectingTokens =
|
||||
"[@0,5:4='<EOF>',<-1>,1:5]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
assertEquals("line 1:0 token recognition error at: '{ { }'\n", stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testInvalidCharAtStartAfterDFACache() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"A : 'a' 'b' ;\n";
|
||||
String tokens = execLexer("L.g4", grammar, "L", "abx");
|
||||
String expectingTokens =
|
||||
"[@0,0:1='ab',<1>,1:0]\n" +
|
||||
"[@1,3:2='<EOF>',<-1>,1:3]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
String expectingError = "line 1:2 token recognition error at: 'x'\n";
|
||||
String error = stderrDuringParse;
|
||||
assertEquals(expectingError, error);
|
||||
}
|
||||
|
||||
@Test public void testInvalidCharInToken() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"A : 'a' 'b' ;\n";
|
||||
String tokens = execLexer("L.g4", grammar, "L", "ax");
|
||||
String expectingTokens =
|
||||
"[@0,2:1='<EOF>',<-1>,1:2]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
String expectingError = "line 1:0 token recognition error at: 'ax'\n";
|
||||
String error = stderrDuringParse;
|
||||
assertEquals(expectingError, error);
|
||||
}
|
||||
|
||||
@Test public void testInvalidCharInTokenAfterDFACache() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"A : 'a' 'b' ;\n";
|
||||
String tokens = execLexer("L.g4", grammar, "L", "abax");
|
||||
String expectingTokens =
|
||||
"[@0,0:1='ab',<1>,1:0]\n" +
|
||||
"[@1,4:3='<EOF>',<-1>,1:4]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
String expectingError = "line 1:2 token recognition error at: 'ax'\n";
|
||||
String error = stderrDuringParse;
|
||||
assertEquals(expectingError, error);
|
||||
}
|
||||
|
||||
@Test public void testDFAToATNThatFailsBackToDFA() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"A : 'ab' ;\n"+
|
||||
"B : 'abc' ;\n";
|
||||
// The first ab caches the DFA then abx goes through the DFA but
|
||||
// into the ATN for the x, which fails. Must go back into DFA
|
||||
// and return to previous dfa accept state
|
||||
String tokens = execLexer("L.g4", grammar, "L", "ababx");
|
||||
String expectingTokens =
|
||||
"[@0,0:1='ab',<1>,1:0]\n" +
|
||||
"[@1,2:3='ab',<1>,1:2]\n" +
|
||||
"[@2,5:4='<EOF>',<-1>,1:5]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
String expectingError = "line 1:4 token recognition error at: 'x'\n";
|
||||
String error = stderrDuringParse;
|
||||
assertEquals(expectingError, error);
|
||||
}
|
||||
|
||||
@Test public void testDFAToATNThatMatchesThenFailsInATN() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"A : 'ab' ;\n"+
|
||||
"B : 'abc' ;\n"+
|
||||
"C : 'abcd' ;\n";
|
||||
// The first ab caches the DFA then abx goes through the DFA but
|
||||
// into the ATN for the c. It marks that hasn't except state
|
||||
// and then keeps going in the ATN. It fails on the x, but
|
||||
// uses the previous accepted in the ATN not DFA
|
||||
String tokens = execLexer("L.g4", grammar, "L", "ababcx");
|
||||
String expectingTokens =
|
||||
"[@0,0:1='ab',<1>,1:0]\n" +
|
||||
"[@1,2:4='abc',<2>,1:2]\n" +
|
||||
"[@2,6:5='<EOF>',<-1>,1:6]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
String expectingError = "line 1:5 token recognition error at: 'x'\n";
|
||||
String error = stderrDuringParse;
|
||||
assertEquals(expectingError, error);
|
||||
}
|
||||
|
||||
@Test public void testErrorInMiddle() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"A : 'abc' ;\n";
|
||||
String tokens = execLexer("L.g4", grammar, "L", "abx");
|
||||
String expectingTokens =
|
||||
"[@0,3:2='<EOF>',<-1>,1:3]\n";
|
||||
assertEquals(expectingTokens, tokens);
|
||||
String expectingError = "line 1:0 token recognition error at: 'abx'\n";
|
||||
String error = stderrDuringParse;
|
||||
assertEquals(expectingError, error);
|
||||
}
|
||||
|
||||
// TEST RECOVERY
|
||||
|
||||
/**
|
||||
* This is a regression test for #45 "NullPointerException in LexerATNSimulator.execDFA".
|
||||
* https://github.com/antlr/antlr4/issues/46
|
||||
*/
|
||||
@Test
|
||||
public void testLexerExecDFA() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : ID ':' expr;\n" +
|
||||
"expr : primary expr? {} | expr '->' ID;\n" +
|
||||
"primary : ID;\n" +
|
||||
"ID : [a-z]+;\n" +
|
||||
"\n";
|
||||
String result = execLexer("T.g4", grammar, "TLexer", "x : x", false);
|
||||
String expecting =
|
||||
"[@0,0:0='x',<3>,1:0]\n" +
|
||||
"[@1,2:2=':',<1>,1:2]\n" +
|
||||
"[@2,4:4='x',<3>,1:4]\n" +
|
||||
"[@3,5:4='<EOF>',<-1>,1:5]\n";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:1 token recognition error at: ' '\n" +
|
||||
"line 1:3 token recognition error at: ' '\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,690 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.antlr.v4.runtime.misc.Nullable;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TestLexerExec extends BaseTest {
|
||||
@Test public void testQuoteTranslation() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"QUOTE : '\"' ;\n"; // make sure this compiles
|
||||
String found = execLexer("L.g4", grammar, "L", "\"");
|
||||
String expecting =
|
||||
"[@0,0:0='\"',<1>,1:0]\n" +
|
||||
"[@1,1:0='<EOF>',<-1>,1:1]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testRefToRuleDoesNotSetTokenNorEmitAnother() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"A : '-' I ;\n" +
|
||||
"I : '0'..'9'+ ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "34 -21 3");
|
||||
String expecting =
|
||||
"[@0,0:1='34',<2>,1:0]\n" +
|
||||
"[@1,3:5='-21',<1>,1:3]\n" +
|
||||
"[@2,7:7='3',<2>,1:7]\n" +
|
||||
"[@3,8:7='<EOF>',<-1>,1:8]\n"; // EOF has no length so range is 8:7 not 8:8
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testSlashes() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"Backslash : '\\\\';\n" +
|
||||
"Slash : '/';\n" +
|
||||
"Vee : '\\\\/';\n" +
|
||||
"Wedge : '/\\\\';\n"+
|
||||
"WS : [ \\t] -> skip;";
|
||||
String found = execLexer("L.g4", grammar, "L", "\\ / \\/ /\\");
|
||||
String expecting =
|
||||
"[@0,0:0='\\',<1>,1:0]\n" +
|
||||
"[@1,2:2='/',<2>,1:2]\n" +
|
||||
"[@2,4:5='\\/',<3>,1:4]\n" +
|
||||
"[@3,7:8='/\\',<4>,1:7]\n" +
|
||||
"[@4,9:8='<EOF>',<-1>,1:9]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#224: "Parentheses without
|
||||
* quantifier in lexer rules have unclear effect".
|
||||
* https://github.com/antlr/antlr4/issues/224
|
||||
*/
|
||||
@Test public void testParentheses() {
|
||||
String grammar =
|
||||
"lexer grammar Demo;\n" +
|
||||
"\n" +
|
||||
"START_BLOCK: '-.-.-';\n" +
|
||||
"\n" +
|
||||
"ID : (LETTER SEPARATOR) (LETTER SEPARATOR)+;\n" +
|
||||
"fragment LETTER: L_A|L_K;\n" +
|
||||
"fragment L_A: '.-';\n" +
|
||||
"fragment L_K: '-.-';\n" +
|
||||
"\n" +
|
||||
"SEPARATOR: '!';\n";
|
||||
String found = execLexer("Demo.g4", grammar, "Demo", "-.-.-!");
|
||||
String expecting =
|
||||
"[@0,0:4='-.-.-',<1>,1:0]\n" +
|
||||
"[@1,5:5='!',<3>,1:5]\n" +
|
||||
"[@2,6:5='<EOF>',<-1>,1:6]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonGreedyTermination() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "STRING : '\"' ('\"\"' | .)*? '\"';";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "\"hi\"\"mom\"");
|
||||
assertEquals(
|
||||
"[@0,0:3='\"hi\"',<1>,1:0]\n" +
|
||||
"[@1,4:8='\"mom\"',<1>,1:4]\n" +
|
||||
"[@2,9:8='<EOF>',<-1>,1:9]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonGreedyTermination2() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "STRING : '\"' ('\"\"' | .)+? '\"';";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "\"\"\"mom\"");
|
||||
assertEquals(
|
||||
"[@0,0:6='\"\"\"mom\"',<1>,1:0]\n" +
|
||||
"[@1,7:6='<EOF>',<-1>,1:7]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreedyOptional() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "CMT : '//' .*? '\\n' CMT?;\n"
|
||||
+ "WS : (' '|'\\t')+;";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "//blah\n//blah\n");
|
||||
assertEquals(
|
||||
"[@0,0:13='//blah\\n//blah\\n',<1>,1:0]\n" +
|
||||
"[@1,14:13='<EOF>',<-1>,3:0]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonGreedyOptional() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "CMT : '//' .*? '\\n' CMT??;\n"
|
||||
+ "WS : (' '|'\\t')+;";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "//blah\n//blah\n");
|
||||
assertEquals(
|
||||
"[@0,0:6='//blah\\n',<1>,1:0]\n" +
|
||||
"[@1,7:13='//blah\\n',<1>,2:0]\n" +
|
||||
"[@2,14:13='<EOF>',<-1>,3:0]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreedyClosure() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "CMT : '//' .*? '\\n' CMT*;\n"
|
||||
+ "WS : (' '|'\\t')+;";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "//blah\n//blah\n");
|
||||
assertEquals(
|
||||
"[@0,0:13='//blah\\n//blah\\n',<1>,1:0]\n" +
|
||||
"[@1,14:13='<EOF>',<-1>,3:0]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonGreedyClosure() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "CMT : '//' .*? '\\n' CMT*?;\n"
|
||||
+ "WS : (' '|'\\t')+;";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "//blah\n//blah\n");
|
||||
assertEquals(
|
||||
"[@0,0:6='//blah\\n',<1>,1:0]\n" +
|
||||
"[@1,7:13='//blah\\n',<1>,2:0]\n" +
|
||||
"[@2,14:13='<EOF>',<-1>,3:0]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreedyPositiveClosure() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "CMT : ('//' .*? '\\n')+;\n"
|
||||
+ "WS : (' '|'\\t')+;";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "//blah\n//blah\n");
|
||||
assertEquals(
|
||||
"[@0,0:13='//blah\\n//blah\\n',<1>,1:0]\n" +
|
||||
"[@1,14:13='<EOF>',<-1>,3:0]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonGreedyPositiveClosure() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"
|
||||
+ "CMT : ('//' .*? '\\n')+?;\n"
|
||||
+ "WS : (' '|'\\t')+;";
|
||||
|
||||
String found = execLexer("L.g4", grammar, "L", "//blah\n//blah\n");
|
||||
assertEquals(
|
||||
"[@0,0:6='//blah\\n',<1>,1:0]\n" +
|
||||
"[@1,7:13='//blah\\n',<1>,2:0]\n" +
|
||||
"[@2,14:13='<EOF>',<-1>,3:0]\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecursiveLexerRuleRefWithWildcardStar1() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"CMT : '/*' (CMT | .)*? '*/' ;\n" +
|
||||
"WS : (' '|'\\n')+ ;\n"
|
||||
/*+ "ANY : .;"*/;
|
||||
|
||||
String expecting =
|
||||
"[@0,0:8='/* ick */',<1>,1:0]\n" +
|
||||
"[@1,9:9='\\n',<2>,1:9]\n" +
|
||||
"[@2,10:34='/* /* */\\n/* /*nested*/ */',<1>,2:0]\n" +
|
||||
"[@3,35:35='\\n',<2>,3:16]\n" +
|
||||
"[@4,36:35='<EOF>',<-1>,4:0]\n";
|
||||
|
||||
// stuff on end of comment matches another rule
|
||||
String found = execLexer("L.g4", grammar, "L",
|
||||
"/* ick */\n" +
|
||||
"/* /* */\n" +
|
||||
"/* /*nested*/ */\n");
|
||||
assertEquals(expecting, found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecursiveLexerRuleRefWithWildcardStar2() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"CMT : '/*' (CMT | .)*? '*/' ;\n" +
|
||||
"WS : (' '|'\\n')+ ;\n"
|
||||
/*+ "ANY : .;"*/;
|
||||
|
||||
// stuff on end of comment doesn't match another rule
|
||||
String expecting =
|
||||
"[@0,0:8='/* ick */',<1>,1:0]\n" +
|
||||
"[@1,10:10='\\n',<2>,1:10]\n" +
|
||||
"[@2,11:36='/* /* */x\\n/* /*nested*/ */',<1>,2:0]\n" +
|
||||
"[@3,38:38='\\n',<2>,3:17]\n" +
|
||||
"[@4,39:38='<EOF>',<-1>,4:0]\n";
|
||||
String found = execLexer("L.g4", grammar, "L",
|
||||
"/* ick */x\n" +
|
||||
"/* /* */x\n" +
|
||||
"/* /*nested*/ */x\n");
|
||||
assertEquals(expecting, found);
|
||||
assertEquals(
|
||||
"line 1:9 token recognition error at: 'x'\n" +
|
||||
"line 3:16 token recognition error at: 'x'\n", stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecursiveLexerRuleRefWithWildcardPlus1() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"CMT : '/*' (CMT | .)+? '*/' ;\n" +
|
||||
"WS : (' '|'\\n')+ ;\n"
|
||||
/*+ "ANY : .;"*/;
|
||||
|
||||
String expecting =
|
||||
"[@0,0:8='/* ick */',<1>,1:0]\n" +
|
||||
"[@1,9:9='\\n',<2>,1:9]\n" +
|
||||
"[@2,10:34='/* /* */\\n/* /*nested*/ */',<1>,2:0]\n" +
|
||||
"[@3,35:35='\\n',<2>,3:16]\n" +
|
||||
"[@4,36:35='<EOF>',<-1>,4:0]\n";
|
||||
|
||||
// stuff on end of comment matches another rule
|
||||
String found = execLexer("L.g4", grammar, "L",
|
||||
"/* ick */\n" +
|
||||
"/* /* */\n" +
|
||||
"/* /*nested*/ */\n");
|
||||
assertEquals(expecting, found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecursiveLexerRuleRefWithWildcardPlus2() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"CMT : '/*' (CMT | .)+? '*/' ;\n" +
|
||||
"WS : (' '|'\\n')+ ;\n"
|
||||
/*+ "ANY : .;"*/;
|
||||
|
||||
// stuff on end of comment doesn't match another rule
|
||||
String expecting =
|
||||
"[@0,0:8='/* ick */',<1>,1:0]\n" +
|
||||
"[@1,10:10='\\n',<2>,1:10]\n" +
|
||||
"[@2,11:36='/* /* */x\\n/* /*nested*/ */',<1>,2:0]\n" +
|
||||
"[@3,38:38='\\n',<2>,3:17]\n" +
|
||||
"[@4,39:38='<EOF>',<-1>,4:0]\n";
|
||||
String found = execLexer("L.g4", grammar, "L",
|
||||
"/* ick */x\n" +
|
||||
"/* /* */x\n" +
|
||||
"/* /*nested*/ */x\n");
|
||||
assertEquals(expecting, found);
|
||||
assertEquals(
|
||||
"line 1:9 token recognition error at: 'x'\n" +
|
||||
"line 3:16 token recognition error at: 'x'\n", stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testActionPlacement() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : ({System.out.println(\"stuff fail: \" + getText());} 'a' | {System.out.println(\"stuff0: \" + getText());} 'a' {System.out.println(\"stuff1: \" + getText());} 'b' {System.out.println(\"stuff2: \" + getText());}) {System.out.println(getText());} ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;\n" +
|
||||
"J : .;\n";
|
||||
String found = execLexer("L.g4", grammar, "L", "ab");
|
||||
String expecting =
|
||||
"stuff0: \n" +
|
||||
"stuff1: a\n" +
|
||||
"stuff2: ab\n" +
|
||||
"ab\n" +
|
||||
"[@0,0:1='ab',<1>,1:0]\n" +
|
||||
"[@1,2:1='<EOF>',<-1>,1:2]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testGreedyConfigs() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : ('a' | 'ab') {System.out.println(getText());} ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;\n" +
|
||||
"J : .;\n";
|
||||
String found = execLexer("L.g4", grammar, "L", "ab");
|
||||
String expecting =
|
||||
"ab\n" +
|
||||
"[@0,0:1='ab',<1>,1:0]\n" +
|
||||
"[@1,2:1='<EOF>',<-1>,1:2]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testNonGreedyConfigs() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : .*? ('a' | 'ab') {System.out.println(getText());} ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;\n" +
|
||||
"J : . {System.out.println(getText());};\n";
|
||||
String found = execLexer("L.g4", grammar, "L", "ab");
|
||||
String expecting =
|
||||
"a\n" +
|
||||
"b\n" +
|
||||
"[@0,0:0='a',<1>,1:0]\n" +
|
||||
"[@1,1:1='b',<3>,1:1]\n" +
|
||||
"[@2,2:1='<EOF>',<-1>,1:2]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testKeywordID() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"KEND : 'end' ;\n" + // has priority
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n')+ ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "end eend ending a");
|
||||
String expecting =
|
||||
"[@0,0:2='end',<1>,1:0]\n" +
|
||||
"[@1,3:3=' ',<3>,1:3]\n" +
|
||||
"[@2,4:7='eend',<2>,1:4]\n" +
|
||||
"[@3,8:8=' ',<3>,1:8]\n" +
|
||||
"[@4,9:14='ending',<2>,1:9]\n" +
|
||||
"[@5,15:15=' ',<3>,1:15]\n" +
|
||||
"[@6,16:16='a',<2>,1:16]\n" +
|
||||
"[@7,17:16='<EOF>',<-1>,1:17]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testHexVsID() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"HexLiteral : '0' ('x'|'X') HexDigit+ ;\n"+
|
||||
"DecimalLiteral : ('0' | '1'..'9' '0'..'9'*) ;\n" +
|
||||
"FloatingPointLiteral : ('0x' | '0X') HexDigit* ('.' HexDigit*)? ;\n" +
|
||||
"DOT : '.' ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"fragment HexDigit : ('0'..'9'|'a'..'f'|'A'..'F') ;\n" +
|
||||
"WS : (' '|'\\n')+ ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "x 0 1 a.b a.l");
|
||||
String expecting =
|
||||
"[@0,0:0='x',<5>,1:0]\n" +
|
||||
"[@1,1:1=' ',<6>,1:1]\n" +
|
||||
"[@2,2:2='0',<2>,1:2]\n" +
|
||||
"[@3,3:3=' ',<6>,1:3]\n" +
|
||||
"[@4,4:4='1',<2>,1:4]\n" +
|
||||
"[@5,5:5=' ',<6>,1:5]\n" +
|
||||
"[@6,6:6='a',<5>,1:6]\n" +
|
||||
"[@7,7:7='.',<4>,1:7]\n" +
|
||||
"[@8,8:8='b',<5>,1:8]\n" +
|
||||
"[@9,9:9=' ',<6>,1:9]\n" +
|
||||
"[@10,10:10='a',<5>,1:10]\n" +
|
||||
"[@11,11:11='.',<4>,1:11]\n" +
|
||||
"[@12,12:12='l',<5>,1:12]\n" +
|
||||
"[@13,13:12='<EOF>',<-1>,1:13]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
// must get DONE EOF
|
||||
@Test public void testEOFByItself() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n" +
|
||||
"DONE : EOF ;\n" +
|
||||
"A : 'a';\n";
|
||||
String found = execLexer("L.g4", grammar, "L", "");
|
||||
String expecting =
|
||||
"[@0,0:-1='<EOF>',<1>,1:0]\n" +
|
||||
"[@1,0:-1='<EOF>',<-1>,1:0]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testEOFSuffixInFirstRule() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"A : 'a' EOF ;\n"+
|
||||
"B : 'a';\n"+
|
||||
"C : 'c';\n";
|
||||
String found = execLexer("L.g4", grammar, "L", "");
|
||||
String expecting =
|
||||
"[@0,0:-1='<EOF>',<-1>,1:0]\n";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
found = execLexer("L.g4", grammar, "L", "a");
|
||||
expecting =
|
||||
"[@0,0:0='a',<1>,1:0]\n" +
|
||||
"[@1,1:0='<EOF>',<-1>,1:1]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSet() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : '0'..'9'+ {System.out.println(\"I\");} ;\n"+
|
||||
"WS : [ \\n\\u000D] -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "34\r\n 34");
|
||||
String expecting =
|
||||
"I\n" +
|
||||
"I\n" +
|
||||
"[@0,0:1='34',<1>,1:0]\n" +
|
||||
"[@1,5:6='34',<1>,2:1]\n" +
|
||||
"[@2,7:6='<EOF>',<-1>,2:3]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetPlus() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : '0'..'9'+ {System.out.println(\"I\");} ;\n"+
|
||||
"WS : [ \\n\\u000D]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "34\r\n 34");
|
||||
String expecting =
|
||||
"I\n" +
|
||||
"I\n" +
|
||||
"[@0,0:1='34',<1>,1:0]\n" +
|
||||
"[@1,5:6='34',<1>,2:1]\n" +
|
||||
"[@2,7:6='<EOF>',<-1>,2:3]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetNot() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : ~[ab \\n] ~[ \\ncd]* {System.out.println(\"I\");} ;\n"+
|
||||
"WS : [ \\n\\u000D]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "xaf");
|
||||
String expecting =
|
||||
"I\n" +
|
||||
"[@0,0:2='xaf',<1>,1:0]\n" +
|
||||
"[@1,3:2='<EOF>',<-1>,1:3]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetInSet() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : (~[ab \\n]|'a') {System.out.println(\"I\");} ;\n"+
|
||||
"WS : [ \\n\\u000D]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "a x");
|
||||
String expecting =
|
||||
"I\n" +
|
||||
"I\n" +
|
||||
"[@0,0:0='a',<1>,1:0]\n" +
|
||||
"[@1,2:2='x',<1>,1:2]\n" +
|
||||
"[@2,3:2='<EOF>',<-1>,1:3]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetRange() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : [0-9]+ {System.out.println(\"I\");} ;\n"+
|
||||
"ID : [a-zA-Z] [a-zA-Z0-9]* {System.out.println(\"ID\");} ;\n"+
|
||||
"WS : [ \\n\\u0009\\r]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "34\r 34 a2 abc \n ");
|
||||
String expecting =
|
||||
"I\n" +
|
||||
"I\n" +
|
||||
"ID\n" +
|
||||
"ID\n" +
|
||||
"[@0,0:1='34',<1>,1:0]\n" +
|
||||
"[@1,4:5='34',<1>,1:4]\n" +
|
||||
"[@2,7:8='a2',<2>,1:7]\n" +
|
||||
"[@3,10:12='abc',<2>,1:10]\n" +
|
||||
"[@4,18:17='<EOF>',<-1>,2:3]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetWithMissingEndRange() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : [0-]+ {System.out.println(\"I\");} ;\n"+
|
||||
"WS : [ \\n\\u000D]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "00\r\n");
|
||||
String expecting =
|
||||
"I\n" +
|
||||
"[@0,0:1='00',<1>,1:0]\n" +
|
||||
"[@1,4:3='<EOF>',<-1>,2:0]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetWithMissingEscapeChar() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"I : [0-9]+ {System.out.println(\"I\");} ;\n"+
|
||||
"WS : [ \\u]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "34 ");
|
||||
String expecting =
|
||||
"I\n" +
|
||||
"[@0,0:1='34',<1>,1:0]\n" +
|
||||
"[@1,3:2='<EOF>',<-1>,1:3]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetWithEscapedChar() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"DASHBRACK : [\\-\\]]+ {System.out.println(\"DASHBRACK\");} ;\n"+
|
||||
"WS : [ \\u]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "- ] ");
|
||||
String expecting =
|
||||
"DASHBRACK\n" +
|
||||
"DASHBRACK\n" +
|
||||
"[@0,0:0='-',<1>,1:0]\n" +
|
||||
"[@1,2:2=']',<1>,1:2]\n" +
|
||||
"[@2,4:3='<EOF>',<-1>,1:4]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetWithReversedRange() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"A : [z-a9]+ {System.out.println(\"A\");} ;\n"+
|
||||
"WS : [ \\u]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "9");
|
||||
String expecting =
|
||||
"A\n" +
|
||||
"[@0,0:0='9',<1>,1:0]\n" +
|
||||
"[@1,1:0='<EOF>',<-1>,1:1]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetWithQuote() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"A : [\"a-z]+ {System.out.println(\"A\");} ;\n"+
|
||||
"WS : [ \\n\\t]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "b\"a");
|
||||
String expecting =
|
||||
"A\n" +
|
||||
"[@0,0:2='b\"a',<1>,1:0]\n" +
|
||||
"[@1,3:2='<EOF>',<-1>,1:3]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetWithQuote2() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"A : [\"\\\\ab]+ {System.out.println(\"A\");} ;\n"+
|
||||
"WS : [ \\n\\t]+ -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "b\"\\a");
|
||||
String expecting =
|
||||
"A\n" +
|
||||
"[@0,0:3='b\"\\a',<1>,1:0]\n" +
|
||||
"[@1,4:3='<EOF>',<-1>,1:4]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionAdjustingLexer() throws Exception {
|
||||
String grammar = load("PositionAdjustingLexer.g4", null);
|
||||
String input =
|
||||
"tokens\n" +
|
||||
"tokens {\n" +
|
||||
"notLabel\n" +
|
||||
"label1 =\n" +
|
||||
"label2 +=\n" +
|
||||
"notLabel\n";
|
||||
String found = execLexer("PositionAdjustingLexer.g4", grammar, "PositionAdjustingLexer", input);
|
||||
|
||||
final int TOKENS = 4;
|
||||
final int LABEL = 5;
|
||||
final int IDENTIFIER = 6;
|
||||
String expecting =
|
||||
"[@0,0:5='tokens',<" + IDENTIFIER + ">,1:0]\n" +
|
||||
"[@1,7:12='tokens',<" + TOKENS + ">,2:0]\n" +
|
||||
"[@2,14:14='{',<3>,2:7]\n" +
|
||||
"[@3,16:23='notLabel',<" + IDENTIFIER + ">,3:0]\n" +
|
||||
"[@4,25:30='label1',<" + LABEL + ">,4:0]\n" +
|
||||
"[@5,32:32='=',<1>,4:7]\n" +
|
||||
"[@6,34:39='label2',<" + LABEL + ">,5:0]\n" +
|
||||
"[@7,41:42='+=',<2>,5:7]\n" +
|
||||
"[@8,44:51='notLabel',<" + IDENTIFIER + ">,6:0]\n" +
|
||||
"[@9,53:52='<EOF>',<-1>,7:0]\n";
|
||||
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#76 "Serialized ATN strings
|
||||
* should be split when longer than 2^16 bytes (class file limitation)"
|
||||
* https://github.com/antlr/antlr4/issues/76
|
||||
*/
|
||||
@Test
|
||||
public void testLargeLexer() throws Exception {
|
||||
StringBuilder grammar = new StringBuilder();
|
||||
grammar.append("lexer grammar L;\n");
|
||||
grammar.append("WS : [ \\t\\r\\n]+ -> skip;\n");
|
||||
for (int i = 0; i < 4000; i++) {
|
||||
grammar.append("KW").append(i).append(" : 'KW' '").append(i).append("';\n");
|
||||
}
|
||||
|
||||
String input = "KW400";
|
||||
String found = execLexer("L.g4", grammar.toString(), "L", input);
|
||||
String expecting =
|
||||
"[@0,0:4='KW400',<402>,1:0]\n" +
|
||||
"[@1,5:4='<EOF>',<-1>,1:5]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#687 "Empty zero-length tokens
|
||||
* cannot have lexer commands" and antlr/antlr4#688 "Lexer cannot match
|
||||
* zero-length tokens"
|
||||
* https://github.com/antlr/antlr4/issues/687
|
||||
* https://github.com/antlr/antlr4/issues/688
|
||||
*/
|
||||
@Test public void testZeroLengthToken() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"\n" +
|
||||
"BeginString\n" +
|
||||
" : '\\'' -> more, pushMode(StringMode)\n" +
|
||||
" ;\n" +
|
||||
"\n" +
|
||||
"mode StringMode;\n" +
|
||||
"\n" +
|
||||
" StringMode_X : 'x' -> more;\n" +
|
||||
" StringMode_Done : -> more, mode(EndStringMode);\n" +
|
||||
"\n" +
|
||||
"mode EndStringMode; \n" +
|
||||
"\n" +
|
||||
" EndString : '\\'' -> popMode;\n";
|
||||
String found = execLexer("L.g4", grammar, "L", "'xxx'");
|
||||
String expecting =
|
||||
"[@0,0:4=''xxx'',<1>,1:0]\n" +
|
||||
"[@1,5:4='<EOF>',<-1>,1:5]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestListeners extends BaseTest {
|
||||
@Test public void testBasic() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@header {import org.antlr.v4.runtime.tree.*;}\n"+
|
||||
"@parser::members {\n" +
|
||||
"public static class LeafListener extends TBaseListener {\n" +
|
||||
" public void visitTerminal(TerminalNode node) {\n" +
|
||||
" System.out.println(node.getSymbol().getText());\n" +
|
||||
" }\n" +
|
||||
" }}\n" +
|
||||
"s\n" +
|
||||
"@after {" +
|
||||
" System.out.println($r.ctx.toStringTree(this));" +
|
||||
" ParseTreeWalker walker = new ParseTreeWalker();\n" +
|
||||
" walker.walk(new LeafListener(), $r.ctx);" +
|
||||
"}\n" +
|
||||
" : r=a ;\n" +
|
||||
"a : INT INT" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"MULT: '*' ;\n" +
|
||||
"ADD : '+' ;\n" +
|
||||
"INT : [0-9]+ ;\n" +
|
||||
"ID : [a-z]+ ;\n" +
|
||||
"WS : [ \\t\\n]+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "1 2", false);
|
||||
String expecting = "(a 1 2)\n" +
|
||||
"1\n" +
|
||||
"2\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testTokenGetters() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {\n" +
|
||||
"public static class LeafListener extends TBaseListener {\n" +
|
||||
" public void exitA(TParser.AContext ctx) {\n" +
|
||||
" if (ctx.getChildCount()==2) System.out.printf(\"%s %s %s\",ctx.INT(0).getSymbol().getText(),ctx.INT(1).getSymbol().getText(),ctx.INT());\n" +
|
||||
" else System.out.println(ctx.ID().getSymbol());\n" +
|
||||
" }\n" +
|
||||
" }}\n" +
|
||||
"s\n" +
|
||||
"@after {" +
|
||||
" System.out.println($r.ctx.toStringTree(this));" +
|
||||
" ParseTreeWalker walker = new ParseTreeWalker();\n" +
|
||||
" walker.walk(new LeafListener(), $r.ctx);" +
|
||||
"}\n" +
|
||||
" : r=a ;\n" +
|
||||
"a : INT INT" +
|
||||
" | ID" +
|
||||
" ;\n" +
|
||||
"MULT: '*' ;\n" +
|
||||
"ADD : '+' ;\n" +
|
||||
"INT : [0-9]+ ;\n" +
|
||||
"ID : [a-z]+ ;\n" +
|
||||
"WS : [ \\t\\n]+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "1 2", false);
|
||||
String expecting =
|
||||
"(a 1 2)\n" +
|
||||
"1 2 [1, 2]\n";
|
||||
assertEquals(expecting, result);
|
||||
|
||||
result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "abc", false);
|
||||
expecting = "(a abc)\n" +
|
||||
"[@0,0:2='abc',<4>,1:0]\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testRuleGetters() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {\n" +
|
||||
"public static class LeafListener extends TBaseListener {\n" +
|
||||
" public void exitA(TParser.AContext ctx) {\n" +
|
||||
" if (ctx.getChildCount()==2) {\n" +
|
||||
" System.out.printf(\"%s %s %s\",ctx.b(0).start.getText(),\n" +
|
||||
" ctx.b(1).start.getText(),ctx.b().get(0).start.getText());\n" +
|
||||
" }\n" +
|
||||
" else System.out.println(ctx.b(0).start.getText());\n" +
|
||||
" }\n" +
|
||||
" }}\n" +
|
||||
"s\n" +
|
||||
"@after {" +
|
||||
" System.out.println($r.ctx.toStringTree(this));" +
|
||||
" ParseTreeWalker walker = new ParseTreeWalker();\n" +
|
||||
" walker.walk(new LeafListener(), $r.ctx);" +
|
||||
"}\n" +
|
||||
" : r=a ;\n" +
|
||||
"a : b b" + // forces list
|
||||
" | b" + // a list still
|
||||
" ;\n" +
|
||||
"b : ID | INT ;\n" +
|
||||
"MULT: '*' ;\n" +
|
||||
"ADD : '+' ;\n" +
|
||||
"INT : [0-9]+ ;\n" +
|
||||
"ID : [a-z]+ ;\n" +
|
||||
"WS : [ \\t\\n]+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "1 2", false);
|
||||
String expecting = "(a (b 1) (b 2))\n" +
|
||||
"1 2 1\n";
|
||||
assertEquals(expecting, result);
|
||||
|
||||
result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "abc", false);
|
||||
expecting = "(a (b abc))\n" +
|
||||
"abc\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLR() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {\n" +
|
||||
"public static class LeafListener extends TBaseListener {\n" +
|
||||
" public void exitE(TParser.EContext ctx) {\n" +
|
||||
" if (ctx.getChildCount()==3) {\n" +
|
||||
" System.out.printf(\"%s %s %s\\n\",ctx.e(0).start.getText(),\n" +
|
||||
" ctx.e(1).start.getText()," +
|
||||
" ctx.e().get(0).start.getText());\n" +
|
||||
" }\n" +
|
||||
" else System.out.println(ctx.INT().getSymbol().getText());\n" +
|
||||
" }\n" +
|
||||
" }" +
|
||||
"}\n" +
|
||||
"s\n" +
|
||||
"@after {" +
|
||||
" System.out.println($r.ctx.toStringTree(this));" +
|
||||
" ParseTreeWalker walker = new ParseTreeWalker();\n" +
|
||||
" walker.walk(new LeafListener(), $r.ctx);" +
|
||||
"}\n" +
|
||||
" : r=e ;\n" +
|
||||
"e : e op='*' e\n" +
|
||||
" | e op='+' e\n" +
|
||||
" | INT\n" +
|
||||
" ;\n" +
|
||||
"MULT: '*' ;\n" +
|
||||
"ADD : '+' ;\n" +
|
||||
"INT : [0-9]+ ;\n" +
|
||||
"WS : [ \\t\\n]+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "1+2*3", false);
|
||||
String expecting =
|
||||
"(e (e 1) + (e (e 2) * (e 3)))\n" +
|
||||
"1\n" +
|
||||
"2\n" +
|
||||
"3\n" +
|
||||
"2 3 2\n" +
|
||||
"1 2 1\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLRWithLabels() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {\n" +
|
||||
" public static class LeafListener extends TBaseListener {\n" +
|
||||
" public void exitCall(TParser.CallContext ctx) {\n" +
|
||||
" System.out.printf(\"%s %s\",ctx.e().start.getText(),\n" +
|
||||
" ctx.eList());\n" +
|
||||
" }\n" +
|
||||
" public void exitInt(TParser.IntContext ctx) {\n" +
|
||||
" System.out.println(ctx.INT().getSymbol().getText());\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"s\n" +
|
||||
"@after {" +
|
||||
" System.out.println($r.ctx.toStringTree(this));" +
|
||||
" ParseTreeWalker walker = new ParseTreeWalker();\n" +
|
||||
" walker.walk(new LeafListener(), $r.ctx);" +
|
||||
"}\n" +
|
||||
" : r=e ;\n" +
|
||||
"e : e '(' eList ')' # Call\n" +
|
||||
" | INT # Int\n" +
|
||||
" ; \n" +
|
||||
"eList : e (',' e)* ;\n" +
|
||||
"MULT: '*' ;\n" +
|
||||
"ADD : '+' ;\n" +
|
||||
"INT : [0-9]+ ;\n" +
|
||||
"WS : [ \\t\\n]+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "1(2,3)", false);
|
||||
String expecting =
|
||||
"(e (e 1) ( (eList (e 2) , (e 3)) ))\n" +
|
||||
"1\n" +
|
||||
"2\n" +
|
||||
"3\n" +
|
||||
"1 [13 6]\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
}
|
|
@ -1,383 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.antlr.v4.runtime.atn.ATNSerializer;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/** test runtime parse errors */
|
||||
public class TestParseErrors extends BaseTest {
|
||||
@Test public void testTokenMismatch() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aa", false);
|
||||
String expecting = "line 1:1 mismatched input 'a' expecting 'b'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSingleTokenDeletion() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aab", false);
|
||||
String expecting = "line 1:1 extraneous input 'a' expecting 'b'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSingleTokenDeletionExpectingSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' ('b'|'c') ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aab", false);
|
||||
String expecting = "line 1:1 extraneous input 'a' expecting {'b', 'c'}\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSingleTokenInsertion() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b' 'c' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "ac", false);
|
||||
String expecting = "line 1:1 missing 'b' at 'c'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testConjuringUpToken() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' x='b' {System.out.println(\"conjured=\"+$x);} 'c' ;";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "a", "ac", false);
|
||||
String expecting = "conjured=[@-1,-1:-1='<missing 'b'>',<2>,1:1]\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSingleSetInsertion() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' ('b'|'c') 'd' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "ad", false);
|
||||
String expecting = "line 1:1 missing {'b', 'c'} at 'd'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testConjuringUpTokenFromSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' x=('b'|'c') {System.out.println(\"conjured=\"+$x);} 'd' ;";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "a", "ad", false);
|
||||
String expecting = "conjured=[@-1,-1:-1='<missing 'b'>',<2>,1:1]\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLL2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'" +
|
||||
" | 'a' 'c'" +
|
||||
";\n" +
|
||||
"q : 'e' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "ae", false);
|
||||
String expecting = "line 1:1 no viable alternative at input 'ae'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLL3() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'* 'c'" +
|
||||
" | 'a' 'b' 'd'" +
|
||||
" ;\n" +
|
||||
"q : 'e' ;\n";
|
||||
System.out.println(grammar);
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "abe", false);
|
||||
String expecting = "line 1:2 no viable alternative at input 'abe'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLLStar() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a'+ 'b'" +
|
||||
" | 'a'+ 'c'" +
|
||||
";\n" +
|
||||
"q : 'e' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aaae", false);
|
||||
String expecting = "line 1:3 no viable alternative at input 'aaae'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSingleTokenDeletionBeforeLoop() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'*;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aabc", false);
|
||||
String expecting = "line 1:1 extraneous input 'a' expecting {<EOF>, 'b'}\n" +
|
||||
"line 1:3 token recognition error at: 'c'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testMultiTokenDeletionBeforeLoop() throws Exception {
|
||||
// can only delete 1 before loop
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'* 'c';";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aacabc", false);
|
||||
String expecting =
|
||||
"line 1:1 extraneous input 'a' expecting {'b', 'c'}\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSingleTokenDeletionDuringLoop() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'* 'c' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "ababbc", false);
|
||||
String expecting = "line 1:2 extraneous input 'a' expecting {'b', 'c'}\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testMultiTokenDeletionDuringLoop() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'* 'c' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "abaaababc", false);
|
||||
String expecting =
|
||||
"line 1:2 extraneous input 'a' expecting {'b', 'c'}\n" +
|
||||
"line 1:6 extraneous input 'a' expecting {'b', 'c'}\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
// ------
|
||||
|
||||
@Test public void testSingleTokenDeletionBeforeLoop2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' ('b'|'z'{;})*;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aabc", false);
|
||||
String expecting = "line 1:1 extraneous input 'a' expecting {<EOF>, 'b', 'z'}\n" +
|
||||
"line 1:3 token recognition error at: 'c'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testMultiTokenDeletionBeforeLoop2() throws Exception {
|
||||
// can only delete 1 before loop
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' ('b'|'z'{;})* 'c';";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "aacabc", false);
|
||||
String expecting =
|
||||
"line 1:1 extraneous input 'a' expecting {'b', 'z', 'c'}\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSingleTokenDeletionDuringLoop2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' ('b'|'z'{;})* 'c' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "ababbc", false);
|
||||
String expecting = "line 1:2 extraneous input 'a' expecting {'b', 'z', 'c'}\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testMultiTokenDeletionDuringLoop2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' ('b'|'z'{;})* 'c' ;";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a", "abaaababc", false);
|
||||
String expecting =
|
||||
"line 1:2 extraneous input 'a' expecting {'b', 'z', 'c'}\n" +
|
||||
"line 1:6 extraneous input 'a' expecting {'b', 'z', 'c'}\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLL1ErrorInfo() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : animal (AND acClass)? service EOF;\n" +
|
||||
"animal : (DOG | CAT );\n" +
|
||||
"service : (HARDWARE | SOFTWARE) ;\n" +
|
||||
"AND : 'and';\n" +
|
||||
"DOG : 'dog';\n" +
|
||||
"CAT : 'cat';\n" +
|
||||
"HARDWARE: 'hardware';\n" +
|
||||
"SOFTWARE: 'software';\n" +
|
||||
"WS : ' ' -> skip ;" +
|
||||
"acClass\n" +
|
||||
"@init\n" +
|
||||
"{ System.out.println(getExpectedTokens().toString(tokenNames)); }\n" +
|
||||
" : ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "start", "dog and software", false);
|
||||
String expecting = "{'hardware', 'software'}\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for #6 "NullPointerException in getMissingSymbol".
|
||||
* https://github.com/antlr/antlr4/issues/6
|
||||
*/
|
||||
@Test
|
||||
public void testInvalidEmptyInput() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : ID+;\n" +
|
||||
"ID : [a-z]+;\n" +
|
||||
"\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "start", "", true);
|
||||
String expecting = "";
|
||||
assertEquals(expecting, result);
|
||||
assertEquals("line 1:0 missing ID at '<EOF>'\n", this.stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression test for "Getter for context is not a list when it should be".
|
||||
* https://github.com/antlr/antlr4/issues/19
|
||||
*/
|
||||
@Test
|
||||
public void testContextListGetters() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members{\n" +
|
||||
" void foo() {\n" +
|
||||
" SContext s = null;\n" +
|
||||
" List<? extends AContext> a = s.a();\n" +
|
||||
" List<? extends BContext> b = s.b();\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"s : (a | b)+;\n" +
|
||||
"a : 'a' {System.out.print('a');};\n" +
|
||||
"b : 'b' {System.out.print('b');};\n" +
|
||||
"";
|
||||
String result = execParser("T.g", grammar, "TParser", "TLexer", "s", "abab", true);
|
||||
String expecting = "abab\n";
|
||||
assertEquals(expecting, result);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for #26 "an exception upon simple rule with double recursion in an alternative".
|
||||
* https://github.com/antlr/antlr4/issues/26
|
||||
*/
|
||||
void testDuplicatedLeftRecursiveCall(String input) throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : expr EOF;\n" +
|
||||
"expr : 'x'\n" +
|
||||
" | expr expr\n" +
|
||||
" ;\n" +
|
||||
"\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "start", input, true);
|
||||
assertEquals("", result);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDuplicatedLeftRecursiveCall1() throws Exception {
|
||||
testDuplicatedLeftRecursiveCall("x");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicatedLeftRecursiveCall2() throws Exception {
|
||||
testDuplicatedLeftRecursiveCall("xx");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicatedLeftRecursiveCall3() throws Exception {
|
||||
testDuplicatedLeftRecursiveCall("xxx");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicatedLeftRecursiveCall4() throws Exception {
|
||||
testDuplicatedLeftRecursiveCall("xxxx");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for #45 "NullPointerException in ATNConfig.hashCode".
|
||||
* https://github.com/antlr/antlr4/issues/45
|
||||
* <p/>
|
||||
* The original cause of this issue was an error in the tool's ATN state optimization,
|
||||
* which is now detected early in {@link ATNSerializer} by ensuring that all
|
||||
* serialized transitions point to states which were not removed.
|
||||
*/
|
||||
@Test
|
||||
public void testInvalidATNStateRemoval() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : ID ':' expr;\n" +
|
||||
"expr : primary expr? {} | expr '->' ID;\n" +
|
||||
"primary : ID;\n" +
|
||||
"ID : [a-z]+;\n" +
|
||||
"\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "start", "x:x", true);
|
||||
String expecting = "";
|
||||
assertEquals(expecting, result);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testNoViableAltAvoidance() throws Exception {
|
||||
// "a." matches 'a' to rule e but then realizes '.' won't match.
|
||||
// previously would cause noviablealt. now prediction pretends to
|
||||
// have "a' predict 2nd alt of e. Will get syntax error later so
|
||||
// let it get farther.
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : e '!' ;\n" +
|
||||
"e : 'a' 'b'\n" +
|
||||
" | 'a'\n" +
|
||||
" ;\n" +
|
||||
"DOT : '.' ;\n" +
|
||||
"WS : [ \\t\\r\\n]+ -> skip;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s", "a.", false);
|
||||
String expecting =
|
||||
"line 1:1 mismatched input '.' expecting '!'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestParseTrees extends BaseTest {
|
||||
@Test public void testTokenAndRuleContextString() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" :r=a ;\n" +
|
||||
"a : 'x' {System.out.println(getRuleInvocationStack());} ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "x", false);
|
||||
String expecting = "[a, s]\n(a x)\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testToken2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" :r=a ;\n" +
|
||||
"a : 'x' 'y'\n" +
|
||||
" ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "xy", false);
|
||||
String expecting = "(a x y)\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void test2Alts() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" :r=a ;\n" +
|
||||
"a : 'x' | 'y'\n" +
|
||||
" ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "y", false);
|
||||
String expecting = "(a y)\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void test2AltLoop() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" :r=a ;\n" +
|
||||
"a : ('x' | 'y')* 'z'\n" +
|
||||
" ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "xyyxyxz", false);
|
||||
String expecting = "(a x y y x y x z)\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testRuleRef() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" : r=a ;\n" +
|
||||
"a : b 'x'\n" +
|
||||
" ;\n" +
|
||||
"b : 'y' ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "yx", false);
|
||||
String expecting = "(a (b y) x)\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
// ERRORS
|
||||
|
||||
@Test public void testExtraToken() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" : r=a ;\n" +
|
||||
"a : 'x' 'y'\n" +
|
||||
" ;\n" +
|
||||
"Z : 'z'; \n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "xzy", false);
|
||||
String expecting = "(a x z y)\n"; // ERRORs not shown. z is colored red in tree view
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testNoViableAlt() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" : r=a ;\n" +
|
||||
"a : 'x' | 'y'\n" +
|
||||
" ;\n" +
|
||||
"Z : 'z'; \n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "z", false);
|
||||
String expecting = "(a z)\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testSync() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s\n" +
|
||||
"@init {setBuildParseTree(true);}\n" +
|
||||
"@after {System.out.println($r.ctx.toStringTree(this));}\n" +
|
||||
" : r=a ;\n" +
|
||||
"a : 'x' 'y'* '!'\n" +
|
||||
" ;\n" +
|
||||
"Z : 'z'; \n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s", "xzyy!", false);
|
||||
String expecting = "(a x z y y !)\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
}
|
|
@ -1,597 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/** Test parser execution.
|
||||
*
|
||||
* For the non-greedy stuff, the rule is that .* or any other non-greedy loop
|
||||
* (any + or * loop that has an alternative with '.' in it is automatically
|
||||
* non-greedy) never sees past the end of the rule containing that loop.
|
||||
* There is no automatic way to detect when the exit branch of a non-greedy
|
||||
* loop has seen enough input to determine how much the loop should consume
|
||||
* yet still allow matching the entire input. Of course, this is extremely
|
||||
* inefficient, particularly for things like
|
||||
*
|
||||
* block : '{' (block|.)* '}' ;
|
||||
*
|
||||
* that need only see one symbol to know when it hits a '}'. So, I
|
||||
* came up with a practical solution. During prediction, the ATN
|
||||
* simulator never fall off the end of a rule to compute the global
|
||||
* FOLLOW. Instead, we terminate the loop, choosing the exit branch.
|
||||
* Otherwise, we predict to reenter the loop. For example, input
|
||||
* "{ foo }" will allow the loop to match foo, but that's it. During
|
||||
* prediction, the ATN simulator will see that '}' reaches the end of a
|
||||
* rule that contains a non-greedy loop and stop prediction. It will choose
|
||||
* the exit branch of the inner loop. So, the way in which you construct
|
||||
* the rule containing a non-greedy loop dictates how far it will scan ahead.
|
||||
* Include everything after the non-greedy loop that you know it must scan
|
||||
* in order to properly make a prediction decision. these beasts are tricky,
|
||||
* so be careful. don't liberally sprinkle them around your code.
|
||||
*
|
||||
* To simulate filter mode, use ( .* (pattern1|pattern2|...) )*
|
||||
*
|
||||
* Nongreedy loops match as much input as possible while still allowing
|
||||
* the remaining input to match.
|
||||
*/
|
||||
public class TestParserExec extends BaseTest {
|
||||
@Test public void testLabels() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : b1=b b2+=b* b3+=';' ;\n" +
|
||||
"b : id=ID val+=INT*;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"abc 34;", false);
|
||||
assertEquals("", found);
|
||||
assertEquals(null, stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for #270 "Fix operator += applied to a set of
|
||||
* tokens".
|
||||
* https://github.com/antlr/antlr4/issues/270
|
||||
*/
|
||||
@Test public void testListLabelOnSet() {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : b b* ';' ;\n" +
|
||||
"b : ID val+=(INT | FLOAT)*;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"FLOAT : [0-9]+ '.' [0-9]+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"abc 34;", false);
|
||||
assertEquals("", found);
|
||||
assertEquals(null, stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testBasic() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ID INT {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"abc 34", false);
|
||||
assertEquals("abc34\n", found);
|
||||
}
|
||||
|
||||
@Test public void testAorB() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ID {System.out.println(\" alt 1\");}" +
|
||||
" | INT {System.out.println(\"alt 2\");}" +
|
||||
";\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"34", false);
|
||||
assertEquals("alt 2\n", found);
|
||||
}
|
||||
|
||||
@Test public void testAPlus() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ID+ {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a b c", false);
|
||||
assertEquals("abc\n", found);
|
||||
}
|
||||
|
||||
// force complex decision
|
||||
@Test public void testAorAPlus() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : (ID|ID)+ {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a b c", false);
|
||||
assertEquals("abc\n", found);
|
||||
}
|
||||
|
||||
private static final String ifIfElseGrammarFormat =
|
||||
"grammar T;\n" +
|
||||
"start : statement+ ;\n" +
|
||||
"statement : 'x' | ifStatement;\n" +
|
||||
"ifStatement : 'if' 'y' statement %s {System.out.println($text);};\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> channel(HIDDEN);\n";
|
||||
|
||||
@Test public void testIfIfElseGreedyBinding1() throws Exception {
|
||||
final String input = "if y if y x else x";
|
||||
final String expectedInnerBound = "if y x else x\nif y if y x else x\n";
|
||||
|
||||
String grammar = String.format(ifIfElseGrammarFormat, "('else' statement)?");
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "start", input, false);
|
||||
assertEquals(expectedInnerBound, found);
|
||||
|
||||
}
|
||||
|
||||
@Test public void testIfIfElseGreedyBinding2() throws Exception {
|
||||
final String input = "if y if y x else x";
|
||||
final String expectedInnerBound = "if y x else x\nif y if y x else x\n";
|
||||
|
||||
String grammar = String.format(ifIfElseGrammarFormat, "('else' statement|)");
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "start", input, false);
|
||||
assertEquals(expectedInnerBound, found);
|
||||
}
|
||||
|
||||
@Test public void testIfIfElseNonGreedyBinding() throws Exception {
|
||||
final String input = "if y if y x else x";
|
||||
final String expectedOuterBound = "if y x\nif y if y x else x\n";
|
||||
|
||||
String grammar = String.format(ifIfElseGrammarFormat, "('else' statement)??");
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "start", input, false);
|
||||
assertEquals(expectedOuterBound, found);
|
||||
|
||||
grammar = String.format(ifIfElseGrammarFormat, "(|'else' statement)");
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "start", input, false);
|
||||
assertEquals(expectedOuterBound, found);
|
||||
}
|
||||
|
||||
@Test public void testAStar() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ID* {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"", false);
|
||||
assertEquals("\n", found);
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a b c", false);
|
||||
assertEquals("abc\n", found);
|
||||
}
|
||||
|
||||
@Test public void testLL1OptionalBlock() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : (ID|{}INT)? {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"", false);
|
||||
assertEquals("\n", found);
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a", false);
|
||||
assertEquals("a\n", found);
|
||||
}
|
||||
|
||||
// force complex decision
|
||||
@Test public void testAorAStar() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : (ID|ID)* {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"", false);
|
||||
assertEquals("\n", found);
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a b c", false);
|
||||
assertEquals("abc\n", found);
|
||||
}
|
||||
|
||||
@Test public void testAorBPlus() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : (ID|INT{;})+ {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a 34 c", false);
|
||||
assertEquals("a34c\n", found);
|
||||
}
|
||||
|
||||
@Test public void testAorBStar() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : (ID|INT{;})* {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"", false);
|
||||
assertEquals("\n", found);
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a 34 c", false);
|
||||
assertEquals("a34c\n", found);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This test is meant to detect regressions of bug antlr/antlr4#41.
|
||||
* https://github.com/antlr/antlr4/issues/41
|
||||
*/
|
||||
@Test
|
||||
public void testOptional1() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"stat : ifstat | 'x';\n" +
|
||||
"ifstat : 'if' stat ('else' stat)?;\n" +
|
||||
"WS : [ \\n\\t]+ -> skip ;"
|
||||
;
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "stat", "x", false);
|
||||
assertEquals("", found);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptional2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"stat : ifstat | 'x';\n" +
|
||||
"ifstat : 'if' stat ('else' stat)?;\n" +
|
||||
"WS : [ \\n\\t]+ -> skip ;"
|
||||
;
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "stat", "if x else x", false);
|
||||
assertEquals("", found);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptional3() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"stat : ifstat | 'x';\n" +
|
||||
"ifstat : 'if' stat ('else' stat)?;\n" +
|
||||
"WS : [ \\n\\t]+ -> skip ;"
|
||||
;
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "stat", "if x", false);
|
||||
assertEquals("", found);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptional4() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"stat : ifstat | 'x';\n" +
|
||||
"ifstat : 'if' stat ('else' stat)?;\n" +
|
||||
"WS : [ \\n\\t]+ -> skip ;"
|
||||
;
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "stat", "if if x else x", false);
|
||||
assertEquals("", found);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test is meant to test the expected solution to antlr/antlr4#42.
|
||||
* https://github.com/antlr/antlr4/issues/42
|
||||
*/
|
||||
@Test
|
||||
public void testPredicatedIfIfElse() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : stmt EOF ;\n" +
|
||||
"stmt : ifStmt | ID;\n" +
|
||||
"ifStmt : 'if' ID stmt ('else' stmt | {_input.LA(1) != ELSE}?);\n" +
|
||||
"ELSE : 'else';\n" +
|
||||
"ID : [a-zA-Z]+;\n" +
|
||||
"WS : [ \\n\\t]+ -> skip;\n"
|
||||
;
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"if x if x a else b", true);
|
||||
String expecting = "";
|
||||
assertEquals(expecting, found);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#118.
|
||||
* https://github.com/antlr/antlr4/issues/118
|
||||
*/
|
||||
@Ignore("Performance impact of passing this test may not be worthwhile")
|
||||
@Test public void testStartRuleWithoutEOF() {
|
||||
String grammar =
|
||||
"grammar T;\n"+
|
||||
"s @after {dumpDFA();}\n" +
|
||||
" : ID | ID INT ID ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"INT : '0'..'9'+ ;\n"+
|
||||
"WS : (' '|'\\t'|'\\n')+ -> skip ;\n";
|
||||
String result = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"abc 34", true);
|
||||
String expecting =
|
||||
"Decision 0:\n" +
|
||||
"s0-ID->s1\n" +
|
||||
"s1-INT->s2\n" +
|
||||
"s2-EOF->:s3=>1\n"; // Must point at accept state
|
||||
assertEquals(expecting, result);
|
||||
assertNull(this.stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#195 "label 'label' type
|
||||
* mismatch with previous definition: TOKEN_LABEL!=RULE_LABEL"
|
||||
* https://github.com/antlr/antlr4/issues/195
|
||||
*/
|
||||
@Test public void testLabelAliasingAcrossLabeledAlternatives() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : a* EOF;\n" +
|
||||
"a\n" +
|
||||
" : label=subrule {System.out.println($label.text);} #One\n" +
|
||||
" | label='y' {System.out.println($label.text);} #Two\n" +
|
||||
" ;\n" +
|
||||
"subrule : 'x';\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "start",
|
||||
"xy", false);
|
||||
assertEquals("x\ny\n", found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#334 "BailErrorStrategy: bails
|
||||
* out on proper input".
|
||||
* https://github.com/antlr/antlr4/issues/334
|
||||
*/
|
||||
@Test public void testPredictionIssue334() {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"\n" +
|
||||
"file @init{setErrorHandler(new BailErrorStrategy());} \n" +
|
||||
"@after {System.out.println($ctx.toStringTree(this));}\n" +
|
||||
" : item (SEMICOLON item)* SEMICOLON? EOF ;\n" +
|
||||
"item : A B?;\n" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
"SEMICOLON: ';';\n" +
|
||||
"\n" +
|
||||
"A : 'a'|'A';\n" +
|
||||
"B : 'b'|'B';\n" +
|
||||
"\n" +
|
||||
"WS : [ \\r\\t\\n]+ -> skip;\n";
|
||||
|
||||
String input = "a";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "file", input, false);
|
||||
assertEquals("(file (item a) <EOF>)\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regressino test for antlr/antlr4#299 "Repeating subtree not
|
||||
* accessible in visitor".
|
||||
* https://github.com/antlr/antlr4/issues/299
|
||||
*/
|
||||
@Test public void testListLabelForClosureContext() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"ifStatement\n" +
|
||||
"@after { List<? extends ElseIfStatementContext> items = $ctx.elseIfStatement(); }\n" +
|
||||
" : 'if' expression\n" +
|
||||
" ( ( 'then'\n" +
|
||||
" executableStatement*\n" +
|
||||
" elseIfStatement* // <--- problem is here\n" +
|
||||
" elseStatement?\n" +
|
||||
" 'end' 'if'\n" +
|
||||
" ) | executableStatement )\n" +
|
||||
" ;\n" +
|
||||
"\n" +
|
||||
"elseIfStatement\n" +
|
||||
" : 'else' 'if' expression 'then' executableStatement*\n" +
|
||||
" ;\n"
|
||||
+ "expression : 'a' ;\n"
|
||||
+ "executableStatement : 'a' ;\n"
|
||||
+ "elseStatement : 'a' ;\n";
|
||||
String input = "a";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "expression", input, false);
|
||||
assertEquals("", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that {@link ParserATNSimulator} produces a correct
|
||||
* result when the grammar contains multiple explicit references to
|
||||
* {@code EOF} inside of parser rules.
|
||||
*/
|
||||
@Test
|
||||
public void testMultipleEOFHandling() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"prog : ('x' | 'x' 'y') EOF EOF;\n";
|
||||
String input = "x";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "prog", input, false);
|
||||
assertEquals("", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that {@link ParserATNSimulator} does not produce a
|
||||
* {@link StackOverflowError} when it encounters an {@code EOF} transition
|
||||
* inside a closure.
|
||||
*/
|
||||
@Test
|
||||
public void testEOFInClosure() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"prog : stat EOF;\n" +
|
||||
"stat : 'x' ('y' | EOF)*?;\n";
|
||||
String input = "x";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "prog", input, false);
|
||||
assertEquals("", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#561 "Issue with parser
|
||||
* generation in 4.2.2"
|
||||
* https://github.com/antlr/antlr4/issues/561
|
||||
*/
|
||||
@Test public void testReferenceToATN() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : (ID|ATN)* ATN? {System.out.println($text);} ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"ATN : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"", false);
|
||||
assertEquals("\n", found);
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "a",
|
||||
"a 34 c", false);
|
||||
assertEquals("a34c\n", found);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#588 "ClassCastException during
|
||||
* semantic predicate handling".
|
||||
* https://github.com/antlr/antlr4/issues/588
|
||||
*/
|
||||
@Test public void testFailedPredicateExceptionState() throws Exception {
|
||||
String grammar = load("Psl.g4", "UTF-8");
|
||||
String found = execParser("Psl.g4", grammar, "PslParser", "PslLexer", "floating_constant", " . 234", false);
|
||||
assertEquals("", found);
|
||||
assertEquals("line 1:6 rule floating_constant DEC:A floating-point constant cannot have internal white space\n", stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#563 "Inconsistent token
|
||||
* handling in ANTLR4".
|
||||
* https://github.com/antlr/antlr4/issues/563
|
||||
*/
|
||||
@Test public void testAlternateQuotes() throws Exception {
|
||||
String lexerGrammar =
|
||||
"lexer grammar ModeTagsLexer;\n" +
|
||||
"\n" +
|
||||
"// Default mode rules (the SEA)\n" +
|
||||
"OPEN : '«' -> mode(ISLAND) ; // switch to ISLAND mode\n" +
|
||||
"TEXT : ~'«'+ ; // clump all text together\n" +
|
||||
"\n" +
|
||||
"mode ISLAND;\n" +
|
||||
"CLOSE : '»' -> mode(DEFAULT_MODE) ; // back to SEA mode \n" +
|
||||
"SLASH : '/' ;\n" +
|
||||
"ID : [a-zA-Z]+ ; // match/send ID in tag to parser\n";
|
||||
String parserGrammar =
|
||||
"parser grammar ModeTagsParser;\n" +
|
||||
"\n" +
|
||||
"options { tokenVocab=ModeTagsLexer; } // use tokens from ModeTagsLexer.g4\n" +
|
||||
"\n" +
|
||||
"file: (tag | TEXT)* ;\n" +
|
||||
"\n" +
|
||||
"tag : '«' ID '»'\n" +
|
||||
" | '«' '/' ID '»'\n" +
|
||||
" ;";
|
||||
|
||||
boolean success = rawGenerateAndBuildRecognizer("ModeTagsLexer.g4",
|
||||
lexerGrammar,
|
||||
null,
|
||||
"ModeTagsLexer");
|
||||
assertTrue(success);
|
||||
|
||||
String found = execParser("ModeTagsParser.g4", parserGrammar, "ModeTagsParser", "ModeTagsLexer", "file", "", false);
|
||||
assertEquals("", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#672 "Initialization failed in
|
||||
* locals".
|
||||
* https://github.com/antlr/antlr4/issues/672
|
||||
*/
|
||||
@Test public void testAttributeValueInitialization() throws Exception {
|
||||
String grammar =
|
||||
"grammar Data; \n" +
|
||||
"\n" +
|
||||
"file : group+ EOF; \n" +
|
||||
"\n" +
|
||||
"group: INT sequence {System.out.println($sequence.values.size());} ; \n" +
|
||||
"\n" +
|
||||
"sequence returns [List<Integer> values = new ArrayList<Integer>()] \n" +
|
||||
" locals[List<Integer> localValues = new ArrayList<Integer>()]\n" +
|
||||
" : (INT {$localValues.add($INT.int);})* {$values.addAll($localValues);}\n" +
|
||||
"; \n" +
|
||||
"\n" +
|
||||
"INT : [0-9]+ ; // match integers \n" +
|
||||
"WS : [ \\t\\n\\r]+ -> skip ; // toss out all whitespace\n";
|
||||
|
||||
String input = "2 9 10 3 1 2 3";
|
||||
String found = execParser("Data.g4", grammar, "DataParser", "DataLexer", "file", input, false);
|
||||
assertEquals("6\n", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestSemPredEvalLexer extends BaseTest {
|
||||
|
||||
@Test public void testDisableRule() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"E1 : 'enum' {false}? ;\n" +
|
||||
"E2 : 'enum' {true}? ;\n" + // winner not E1 or ID
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "enum abc", true);
|
||||
String expecting =
|
||||
"[@0,0:3='enum',<2>,1:0]\n" +
|
||||
"[@1,5:7='abc',<3>,1:5]\n" +
|
||||
"[@2,8:7='<EOF>',<-1>,1:8]\n" +
|
||||
"s0-' '->:s5=>4\n" +
|
||||
"s0-'a'->:s6=>3\n" +
|
||||
"s0-'e'->:s1=>3\n" +
|
||||
":s1=>3-'n'->:s2=>3\n" +
|
||||
":s2=>3-'u'->:s3=>3\n" +
|
||||
":s6=>3-'b'->:s6=>3\n" +
|
||||
":s6=>3-'c'->:s6=>3\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testIDvsEnum() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"ENUM : 'enum' {false}? ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "enum abc enum", true);
|
||||
String expecting =
|
||||
"[@0,0:3='enum',<2>,1:0]\n" +
|
||||
"[@1,5:7='abc',<2>,1:5]\n" +
|
||||
"[@2,9:12='enum',<2>,1:9]\n" +
|
||||
"[@3,13:12='<EOF>',<-1>,1:13]\n" +
|
||||
"s0-' '->:s5=>3\n" +
|
||||
"s0-'a'->:s4=>2\n" +
|
||||
"s0-'e'->:s1=>2\n" +
|
||||
":s1=>2-'n'->:s2=>2\n" +
|
||||
":s2=>2-'u'->:s3=>2\n" +
|
||||
":s4=>2-'b'->:s4=>2\n" +
|
||||
":s4=>2-'c'->:s4=>2\n"; // no 'm'-> transition...conflicts with pred
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testIDnotEnum() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"ENUM : [a-z]+ {false}? ;\n" +
|
||||
"ID : [a-z]+ ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "enum abc enum", true);
|
||||
String expecting =
|
||||
"[@0,0:3='enum',<2>,1:0]\n" +
|
||||
"[@1,5:7='abc',<2>,1:5]\n" +
|
||||
"[@2,9:12='enum',<2>,1:9]\n" +
|
||||
"[@3,13:12='<EOF>',<-1>,1:13]\n" +
|
||||
"s0-' '->:s2=>3\n"; // no edges in DFA for enum/id. all paths lead to pred.
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testEnumNotID() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"ENUM : [a-z]+ {getText().equals(\"enum\")}? ;\n" +
|
||||
"ID : [a-z]+ ;\n"+
|
||||
"WS : (' '|'\\n') -> skip ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "enum abc enum", true);
|
||||
String expecting =
|
||||
"[@0,0:3='enum',<1>,1:0]\n" +
|
||||
"[@1,5:7='abc',<2>,1:5]\n" +
|
||||
"[@2,9:12='enum',<1>,1:9]\n" +
|
||||
"[@3,13:12='<EOF>',<-1>,1:13]\n" +
|
||||
"s0-' '->:s3=>3\n"; // no edges in DFA for enum/id. all paths lead to pred.
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testIndent() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"ID : [a-z]+ ;\n"+
|
||||
"INDENT : [ \\t]+ {_tokenStartCharPositionInLine==0}? \n" +
|
||||
" {System.out.println(\"INDENT\");} ;"+
|
||||
"NL : '\\n' ;"+
|
||||
"WS : [ \\t]+ ;";
|
||||
String found = execLexer("L.g4", grammar, "L", "abc\n def \n", true);
|
||||
String expecting =
|
||||
"INDENT\n" + // action output
|
||||
"[@0,0:2='abc',<1>,1:0]\n" + // ID
|
||||
"[@1,3:3='\\n',<3>,1:3]\n" + // NL
|
||||
"[@2,4:5=' ',<2>,2:0]\n" + // INDENT
|
||||
"[@3,6:8='def',<1>,2:2]\n" + // ID
|
||||
"[@4,9:10=' ',<4>,2:5]\n" + // WS
|
||||
"[@5,11:11='\\n',<3>,2:7]\n" +
|
||||
"[@6,12:11='<EOF>',<-1>,3:0]\n" +
|
||||
"s0-'\n" +
|
||||
"'->:s2=>3\n" +
|
||||
"s0-'a'->:s1=>1\n" +
|
||||
"s0-'d'->:s1=>1\n" +
|
||||
":s1=>1-'b'->:s1=>1\n" +
|
||||
":s1=>1-'c'->:s1=>1\n" +
|
||||
":s1=>1-'e'->:s1=>1\n" +
|
||||
":s1=>1-'f'->:s1=>1\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testLexerInputPositionSensitivePredicates() throws Exception {
|
||||
String grammar =
|
||||
"lexer grammar L;\n"+
|
||||
"WORD1 : ID1+ {System.out.println(getText());} ;\n"+
|
||||
"WORD2 : ID2+ {System.out.println(getText());} ;\n"+
|
||||
"fragment ID1 : {getCharPositionInLine()<2}? [a-zA-Z];\n"+
|
||||
"fragment ID2 : {getCharPositionInLine()>=2}? [a-zA-Z];\n"+
|
||||
"WS : (' '|'\\n') -> skip;\n";
|
||||
String found = execLexer("L.g4", grammar, "L", "a cde\nabcde\n");
|
||||
String expecting =
|
||||
"a\n" +
|
||||
"cde\n" +
|
||||
"ab\n" +
|
||||
"cde\n" +
|
||||
"[@0,0:0='a',<1>,1:0]\n" +
|
||||
"[@1,2:4='cde',<2>,1:2]\n" +
|
||||
"[@2,6:7='ab',<1>,2:0]\n" +
|
||||
"[@3,8:10='cde',<2>,2:2]\n" +
|
||||
"[@4,12:11='<EOF>',<-1>,3:0]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testPredicatedKeywords() {
|
||||
String grammar =
|
||||
"lexer grammar A;" +
|
||||
"ENUM : [a-z]+ {getText().equals(\"enum\")}? {System.out.println(\"enum!\");} ;\n" +
|
||||
"ID : [a-z]+ {System.out.println(\"ID \"+getText());} ;\n" +
|
||||
"WS : [ \\n] -> skip ;";
|
||||
String found = execLexer("A.g4", grammar, "A", "enum enu a");
|
||||
String expecting =
|
||||
"enum!\n" +
|
||||
"ID enu\n" +
|
||||
"ID a\n" +
|
||||
"[@0,0:3='enum',<1>,1:0]\n" +
|
||||
"[@1,5:7='enu',<2>,1:5]\n" +
|
||||
"[@2,9:9='a',<2>,1:9]\n" +
|
||||
"[@3,10:9='<EOF>',<-1>,1:10]\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
}
|
|
@ -1,626 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TestSemPredEvalParser extends BaseTest {
|
||||
// TEST VALIDATING PREDS
|
||||
|
||||
@Test public void testSimpleValidate() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a ;\n" +
|
||||
"a : {false}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {true}? INT {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
/*String found = */execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x", false);
|
||||
|
||||
String expecting = "line 1:0 no viable alternative at input 'x'\n";
|
||||
assertEquals(expecting, stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testSimpleValidate2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a a a;\n" +
|
||||
"a : {false}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {true}? INT {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"3 4 x", false);
|
||||
String expecting =
|
||||
"alt 2\n" +
|
||||
"alt 2\n";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
expecting = "line 1:4 no viable alternative at input 'x'\n";
|
||||
assertEquals(expecting, stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#196
|
||||
* "element+ in expression grammar doesn't parse properly"
|
||||
* https://github.com/antlr/antlr4/issues/196
|
||||
*/
|
||||
@Test public void testAtomWithClosureInTranslatedLRRule() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : e[0] EOF;\n" +
|
||||
"e[int _p]\n" +
|
||||
" : ( 'a'\n" +
|
||||
" | 'b'+\n" +
|
||||
" )\n" +
|
||||
" ( {3 >= $_p}? '+' e[4]\n" +
|
||||
" )*\n" +
|
||||
" ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "start",
|
||||
"a+b+a", false);
|
||||
String expecting = "";
|
||||
assertEquals(expecting, found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testValidateInDFA() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a ';' a;\n" +
|
||||
// ';' helps us to resynchronize without consuming
|
||||
// 2nd 'a' reference. We our testing that the DFA also
|
||||
// throws an exception if the validating predicate fails
|
||||
"a : {false}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {true}? INT {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x ; y", false);
|
||||
String expecting = "";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
expecting =
|
||||
"line 1:0 no viable alternative at input 'x'\n" +
|
||||
"line 1:4 no viable alternative at input 'y'\n";
|
||||
assertEquals(expecting, stderrDuringParse);
|
||||
}
|
||||
|
||||
// TEST DISAMBIG PREDS
|
||||
|
||||
@Test public void testSimple() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a a a;\n" + // do 3x: once in ATN, next in DFA then INT in ATN
|
||||
"a : {false}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {true}? ID {System.out.println(\"alt 2\");}\n" +
|
||||
" | INT {System.out.println(\"alt 3\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x y 3", false);
|
||||
String expecting =
|
||||
"alt 2\n" +
|
||||
"alt 2\n" +
|
||||
"alt 3\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testOrder() throws Exception {
|
||||
// Under new predicate ordering rules (see antlr/antlr4#29), the first
|
||||
// alt with an acceptable config (unpredicated, or predicated and evaluates
|
||||
// to true) is chosen.
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a {} a;\n" + // do 2x: once in ATN, next in DFA;
|
||||
// action blocks lookahead from falling off of 'a'
|
||||
// and looking into 2nd 'a' ref. !ctx dependent pred
|
||||
"a : ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {true}? ID {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x y", false);
|
||||
String expecting =
|
||||
"alt 1\n" +
|
||||
"alt 1\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void test2UnpredicatedAlts() throws Exception {
|
||||
// We have n-2 predicates for n alternatives. pick first alt
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@header {" +
|
||||
"import java.util.*;" +
|
||||
"}" +
|
||||
"s : {_interp.setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);}\n" +
|
||||
" a ';' a;\n" + // do 2x: once in ATN, next in DFA
|
||||
"a : ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | ID {System.out.println(\"alt 2\");}\n" +
|
||||
" | {false}? ID {System.out.println(\"alt 3\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x; y", true);
|
||||
String expecting =
|
||||
"alt 1\n" +
|
||||
"alt 1\n";
|
||||
assertEquals(expecting, found);
|
||||
assertEquals("line 1:0 reportAttemptingFullContext d=0 (a), input='x'\n" +
|
||||
"line 1:0 reportAmbiguity d=0 (a): ambigAlts={1, 2}, input='x'\n" +
|
||||
"line 1:3 reportAttemptingFullContext d=0 (a), input='y'\n" +
|
||||
"line 1:3 reportAmbiguity d=0 (a): ambigAlts={1, 2}, input='y'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void test2UnpredicatedAltsAndOneOrthogonalAlt() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@header {" +
|
||||
"import java.util.*;" +
|
||||
"}" +
|
||||
"s : {_interp.setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);}\n" +
|
||||
" a ';' a ';' a;\n" +
|
||||
"a : INT {System.out.println(\"alt 1\");}\n" +
|
||||
" | ID {System.out.println(\"alt 2\");}\n" + // must pick this one for ID since pred is false
|
||||
" | ID {System.out.println(\"alt 3\");}\n" +
|
||||
" | {false}? ID {System.out.println(\"alt 4\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"34; x; y", true);
|
||||
String expecting =
|
||||
"alt 1\n" +
|
||||
"alt 2\n" +
|
||||
"alt 2\n";
|
||||
assertEquals(expecting, found);
|
||||
assertEquals("line 1:4 reportAttemptingFullContext d=0 (a), input='x'\n" +
|
||||
"line 1:4 reportAmbiguity d=0 (a): ambigAlts={2, 3}, input='x'\n" +
|
||||
"line 1:7 reportAttemptingFullContext d=0 (a), input='y'\n" +
|
||||
"line 1:7 reportAmbiguity d=0 (a): ambigAlts={2, 3}, input='y'\n",
|
||||
this.stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testRewindBeforePredEval() throws Exception {
|
||||
// The parser consumes ID and moves to the 2nd token INT.
|
||||
// To properly evaluate the predicates after matching ID INT,
|
||||
// we must correctly see come back to starting index so LT(1) works
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a a;\n" +
|
||||
"a : {_input.LT(1).getText().equals(\"x\")}? ID INT {System.out.println(\"alt 1\");}\n" +
|
||||
" | {_input.LT(1).getText().equals(\"y\")}? ID INT {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"y 3 x 4", false);
|
||||
String expecting =
|
||||
"alt 2\n" +
|
||||
"alt 1\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testNoTruePredsThrowsNoViableAlt() throws Exception {
|
||||
// checks that we throw exception if all alts
|
||||
// are covered with a predicate and none succeeds
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a a;\n" +
|
||||
"a : {false}? ID INT {System.out.println(\"alt 1\");}\n" +
|
||||
" | {false}? ID INT {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"y 3 x 4", false);
|
||||
String expecting = "line 1:0 no viable alternative at input 'y'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testToLeft() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a+ ;\n" +
|
||||
"a : {false}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {true}? ID {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x x y", false);
|
||||
String expecting =
|
||||
"alt 2\n" +
|
||||
"alt 2\n" +
|
||||
"alt 2\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnpredicatedPathsInAlt() throws Exception{
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a {System.out.println(\"alt 1\");}\n" +
|
||||
" | b {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"a : {false}? ID INT\n" +
|
||||
" | ID INT\n" +
|
||||
" ;\n" +
|
||||
"b : ID ID\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x 4", false);
|
||||
String expecting =
|
||||
"alt 1\n";
|
||||
assertEquals(expecting, found);
|
||||
|
||||
expecting = null;
|
||||
assertEquals(expecting, stderrDuringParse);
|
||||
}
|
||||
|
||||
@Test public void testActionHidesPreds() throws Exception {
|
||||
// can't see preds, resolves to first alt found (1 in this case)
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {int i;}\n" +
|
||||
"s : a+ ;\n" +
|
||||
"a : {i=1;} ID {i==1}? {System.out.println(\"alt 1\");}\n" +
|
||||
" | {i=2;} ID {i==2}? {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x x y", false);
|
||||
String expecting =
|
||||
"alt 1\n" +
|
||||
"alt 1\n" +
|
||||
"alt 1\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/** In this case, we use predicates that depend on global information
|
||||
* like we would do for a symbol table. We simply execute
|
||||
* the predicates assuming that all necessary information is available.
|
||||
* The i++ action is done outside of the prediction and so it is executed.
|
||||
*/
|
||||
@Test public void testToLeftWithVaryingPredicate() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {int i=0;}\n" +
|
||||
"s : ({i++; System.out.println(\"i=\"+i);} a)+ ;\n" +
|
||||
"a : {i % 2 == 0}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {i % 2 != 0}? ID {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"x x y", false);
|
||||
String expecting =
|
||||
"i=1\n" +
|
||||
"alt 2\n" +
|
||||
"i=2\n" +
|
||||
"alt 1\n" +
|
||||
"i=3\n" +
|
||||
"alt 2\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/**
|
||||
* In this case, we're passing a parameter into a rule that uses that
|
||||
* information to predict the alternatives. This is the special case
|
||||
* where we know exactly which context we are in. The context stack
|
||||
* is empty and we have not dipped into the outer context to make a decision.
|
||||
*/
|
||||
@Test public void testPredicateDependentOnArg() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {int i=0;}\n" +
|
||||
"s : a[2] a[1];\n" +
|
||||
"a[int i]" +
|
||||
" : {$i==1}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {$i==2}? ID {System.out.println(\"alt 2\");}\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"a b", false);
|
||||
String expecting =
|
||||
"alt 2\n" +
|
||||
"alt 1\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/** In this case, we have to ensure that the predicates are not
|
||||
tested during the closure after recognizing the 1st ID. The
|
||||
closure will fall off the end of 'a' 1st time and reach into the
|
||||
a[1] rule invocation. It should not execute predicates because it
|
||||
does not know what the parameter is. The context stack will not
|
||||
be empty and so they should be ignored. It will not affect
|
||||
recognition, however. We are really making sure the ATN
|
||||
simulation doesn't crash with context object issues when it
|
||||
encounters preds during FOLLOW.
|
||||
*/
|
||||
@Test public void testPredicateDependentOnArg2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : a[2] a[1];\n" +
|
||||
"a[int i]" +
|
||||
" : {$i==1}? ID\n" +
|
||||
" | {$i==2}? ID\n" +
|
||||
" ;\n" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"a b", false);
|
||||
String expecting =
|
||||
"";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testDependentPredNotInOuterCtxShouldBeIgnored() throws Exception {
|
||||
// uses ID ';' or ID '.' lookahead to solve s. preds not tested.
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : b[2] ';' | b[2] '.' ;\n" + // decision in s drills down to ctx-dependent pred in a;
|
||||
"b[int i] : a[i] ;\n" +
|
||||
"a[int i]" +
|
||||
" : {$i==1}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {$i==2}? ID {System.out.println(\"alt 2\");}\n" +
|
||||
" ;" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"a;", false);
|
||||
String expecting =
|
||||
"alt 2\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testIndependentPredNotPassedOuterCtxToAvoidCastException() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"s : b ';' | b '.' ;\n" +
|
||||
"b : a ;\n" +
|
||||
"a" +
|
||||
" : {false}? ID {System.out.println(\"alt 1\");}\n" +
|
||||
" | {true}? ID {System.out.println(\"alt 2\");}\n" +
|
||||
" ;" +
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"a;", false);
|
||||
String expecting =
|
||||
"alt 2\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/** During a global follow operation, we still collect semantic
|
||||
* predicates as long as they are not dependent on local context
|
||||
*/
|
||||
@Test public void testPredsInGlobalFOLLOW() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {" +
|
||||
"void f(Object s) {System.out.println(s);}\n" +
|
||||
"boolean p(boolean v) {System.out.println(\"eval=\"+v); return v;}\n" +
|
||||
"}\n" +
|
||||
"s : e {p(true)}? {f(\"parse\");} '!' ;\n" +
|
||||
"t : e {p(false)}? ID ;\n" +
|
||||
"e : ID | ;\n" + // non-LL(1) so we use ATN
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"a!", false);
|
||||
String expecting =
|
||||
"eval=true\n" + // now we are parsing
|
||||
"parse\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/** We cannot collect predicates that are dependent on local context if
|
||||
* we are doing a global follow. They appear as if they were not there at all.
|
||||
*/
|
||||
@Test public void testDepedentPredsInGlobalFOLLOW() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {" +
|
||||
"void f(Object s) {System.out.println(s);}\n" +
|
||||
"boolean p(boolean v) {System.out.println(\"eval=\"+v); return v;}\n" +
|
||||
"}\n" +
|
||||
"s : a[99] ;\n" +
|
||||
"a[int i] : e {p($i==99)}? {f(\"parse\");} '!' ;\n" +
|
||||
"b[int i] : e {p($i==99)}? ID ;\n" +
|
||||
"e : ID | ;\n" + // non-LL(1) so we use ATN
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"a!", false);
|
||||
String expecting =
|
||||
"eval=true\n" +
|
||||
"parse\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
/** Regular non-forced actions can create side effects used by semantic
|
||||
* predicates and so we cannot evaluate any semantic predicate
|
||||
* encountered after having seen a regular action. This includes
|
||||
* during global follow operations.
|
||||
*/
|
||||
@Test public void testActionsHidePredsInGlobalFOLLOW() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"@parser::members {" +
|
||||
"void f(Object s) {System.out.println(s);}\n" +
|
||||
"boolean p(boolean v) {System.out.println(\"eval=\"+v); return v;}\n" +
|
||||
"}\n" +
|
||||
"s : e {} {p(true)}? {f(\"parse\");} '!' ;\n" +
|
||||
"t : e {} {p(false)}? ID ;\n" +
|
||||
"e : ID | ;\n" + // non-LL(1) so we use ATN
|
||||
"ID : 'a'..'z'+ ;\n" +
|
||||
"INT : '0'..'9'+;\n" +
|
||||
"WS : (' '|'\\n') -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
||||
"a!", false);
|
||||
String expecting =
|
||||
"eval=true\n" +
|
||||
"parse\n";
|
||||
assertEquals(expecting, found);
|
||||
}
|
||||
|
||||
@Test public void testPredTestedEvenWhenUnAmbig() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"\n" +
|
||||
"@parser::members {boolean enumKeyword = true;}\n" +
|
||||
"\n" +
|
||||
"primary\n" +
|
||||
" : ID {System.out.println(\"ID \"+$ID.text);}\n" +
|
||||
" | {!enumKeyword}? 'enum' {System.out.println(\"enum\");}\n" +
|
||||
" ;\n" +
|
||||
"\n" +
|
||||
"ID : [a-z]+ ;\n" +
|
||||
"\n" +
|
||||
"WS : [ \\t\\n\\r]+ -> skip ;\n";
|
||||
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "primary",
|
||||
"abc", false);
|
||||
assertEquals("ID abc\n", found);
|
||||
|
||||
execParser("T.g4", grammar, "TParser", "TLexer", "primary",
|
||||
"enum", false);
|
||||
assertEquals("line 1:0 no viable alternative at input 'enum'\n", stderrDuringParse);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a regression test for antlr/antlr4#218 "ANTLR4 EOF Related Bug".
|
||||
* https://github.com/antlr/antlr4/issues/218
|
||||
*/
|
||||
@Test public void testDisabledAlternative() {
|
||||
String grammar =
|
||||
"grammar AnnotProcessor;\n" +
|
||||
"\n" +
|
||||
"cppCompilationUnit : content+ EOF;\n" +
|
||||
"\n" +
|
||||
"content: anything | {false}? .;\n" +
|
||||
"\n" +
|
||||
"anything: ANY_CHAR;\n" +
|
||||
"\n" +
|
||||
"ANY_CHAR: [_a-zA-Z0-9];\n";
|
||||
|
||||
String input = "hello";
|
||||
String found = execParser("AnnotProcessor.g4", grammar, "AnnotProcessorParser", "AnnotProcessorLexer", "cppCompilationUnit",
|
||||
input, false);
|
||||
assertEquals("", found);
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
|
||||
/** Loopback doesn't eval predicate at start of alt */
|
||||
@Test public void testPredFromAltTestedInLoopBack() {
|
||||
String grammar =
|
||||
"grammar T2;\n" +
|
||||
"\n" +
|
||||
"file\n" +
|
||||
"@after {System.out.println($ctx.toStringTree(this));}\n" +
|
||||
" : para para EOF ;" +
|
||||
"para: paraContent NL NL ;\n"+
|
||||
"paraContent : ('s'|'x'|{_input.LA(2)!=NL}? NL)+ ;\n"+
|
||||
"NL : '\\n' ;\n"+
|
||||
"S : 's' ;\n"+
|
||||
"X : 'x' ;\n";
|
||||
|
||||
String input = "s\n\n\nx\n";
|
||||
String found = execParser("T2.g4", grammar, "T2Parser", "T2Lexer", "file",
|
||||
input, true);
|
||||
assertEquals("(file (para (paraContent s) \\n \\n) (para (paraContent \\n x \\n)) <EOF>)\n", found);
|
||||
assertEquals(stderrDuringParse, "line 5:0 mismatched input '<EOF>' expecting '\n'\n");
|
||||
|
||||
input = "s\n\n\nx\n\n";
|
||||
found = execParser("T2.g4", grammar, "T2Parser", "T2Lexer", "file",
|
||||
input, true);
|
||||
assertEquals("(file (para (paraContent s) \\n \\n) (para (paraContent \\n x) \\n \\n) <EOF>)\n", found);
|
||||
|
||||
assertNull(stderrDuringParse);
|
||||
}
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Terence Parr
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* 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.v4.test;
|
||||
|
||||
import org.antlr.v4.tool.ErrorType;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/** Test the set stuff in lexer and parser */
|
||||
public class TestSets extends BaseTest {
|
||||
protected boolean debug = false;
|
||||
|
||||
/** Public default constructor used by TestRig */
|
||||
public TestSets() {
|
||||
}
|
||||
|
||||
@Test public void testSeqDoesNotBecomeSet() throws Exception {
|
||||
// this must return A not I to the parser; calling a nonfragment rule
|
||||
// from a nonfragment rule does not set the overall token.
|
||||
String grammar =
|
||||
"grammar P;\n" +
|
||||
"a : C {System.out.println(_input.getText());} ;\n" +
|
||||
"fragment A : '1' | '2';\n" +
|
||||
"fragment B : '3' '4';\n" +
|
||||
"C : A | B;\n";
|
||||
String found = execParser("P.g4", grammar, "PParser", "PLexer",
|
||||
"a", "34", debug);
|
||||
assertEquals("34\n", found);
|
||||
}
|
||||
|
||||
@Test public void testParserSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : t=('x'|'y') {System.out.println($t.text);} ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x", debug);
|
||||
assertEquals("x\n", found);
|
||||
}
|
||||
|
||||
@Test public void testParserNotSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : t=~('x'|'y') 'z' {System.out.println($t.text);} ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "zz", debug);
|
||||
assertEquals("z\n", found);
|
||||
}
|
||||
|
||||
@Test public void testParserNotToken() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ~'x' 'z' {System.out.println(_input.getText());} ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "zz", debug);
|
||||
assertEquals("zz\n", found);
|
||||
}
|
||||
|
||||
@Test public void testParserNotTokenWithLabel() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : t=~'x' 'z' {System.out.println($t.text);} ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "zz", debug);
|
||||
assertEquals("z\n", found);
|
||||
}
|
||||
|
||||
@Test public void testRuleAsSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a @after {System.out.println(_input.getText());} : 'a' | 'b' |'c' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "b", debug);
|
||||
assertEquals("b\n", found);
|
||||
}
|
||||
|
||||
@Test public void testNotChar() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println($A.text);} ;\n" +
|
||||
"A : ~'b' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x", debug);
|
||||
assertEquals("x\n", found);
|
||||
}
|
||||
|
||||
@Test public void testOptionalSingleElement() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A? 'c' {System.out.println(_input.getText());} ;\n" +
|
||||
"A : 'b' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "bc", debug);
|
||||
assertEquals("bc\n", found);
|
||||
}
|
||||
|
||||
@Test public void testOptionalLexerSingleElement() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println(_input.getText());} ;\n" +
|
||||
"A : 'b'? 'c' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "bc", debug);
|
||||
assertEquals("bc\n", found);
|
||||
}
|
||||
|
||||
@Test public void testStarLexerSingleElement() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println(_input.getText());} ;\n" +
|
||||
"A : 'b'* 'c' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "bbbbc", debug);
|
||||
assertEquals("bbbbc\n", found);
|
||||
found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "c", debug);
|
||||
assertEquals("c\n", found);
|
||||
}
|
||||
|
||||
@Test public void testPlusLexerSingleElement() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println(_input.getText());} ;\n" +
|
||||
"A : 'b'+ 'c' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "bbbbc", debug);
|
||||
assertEquals("bbbbc\n", found);
|
||||
}
|
||||
|
||||
@Test public void testOptionalSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ('a'|'b')? 'c' {System.out.println(_input.getText());} ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "ac", debug);
|
||||
assertEquals("ac\n", found);
|
||||
}
|
||||
|
||||
@Test public void testStarSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ('a'|'b')* 'c' {System.out.println(_input.getText());} ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "abaac", debug);
|
||||
assertEquals("abaac\n", found);
|
||||
}
|
||||
|
||||
@Test public void testPlusSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : ('a'|'b')+ 'c' {System.out.println(_input.getText());} ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "abaac", debug);
|
||||
assertEquals("abaac\n", found);
|
||||
}
|
||||
|
||||
@Test public void testLexerOptionalSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println(_input.getText());} ;\n" +
|
||||
"A : ('a'|'b')? 'c' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "ac", debug);
|
||||
assertEquals("ac\n", found);
|
||||
}
|
||||
|
||||
@Test public void testLexerStarSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println(_input.getText());} ;\n" +
|
||||
"A : ('a'|'b')* 'c' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "abaac", debug);
|
||||
assertEquals("abaac\n", found);
|
||||
}
|
||||
|
||||
@Test public void testLexerPlusSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println(_input.getText());} ;\n" +
|
||||
"A : ('a'|'b')+ 'c' ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "abaac", debug);
|
||||
assertEquals("abaac\n", found);
|
||||
}
|
||||
|
||||
@Test public void testNotCharSet() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println($A.text);} ;\n" +
|
||||
"A : ~('b'|'c') ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x", debug);
|
||||
assertEquals("x\n", found);
|
||||
}
|
||||
|
||||
@Test public void testNotCharSetWithLabel() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println($A.text);} ;\n" +
|
||||
"A : h=~('b'|'c') ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x", debug);
|
||||
assertEquals("x\n", found);
|
||||
}
|
||||
|
||||
@Test public void testNotCharSetWithRuleRef() throws Exception {
|
||||
// might be a useful feature to add someday
|
||||
String[] pair = new String[] {
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println($A.text);} ;\n" +
|
||||
"A : ~('a'|B) ;\n" +
|
||||
"B : 'b' ;\n",
|
||||
"error(" + ErrorType.UNSUPPORTED_REFERENCE_IN_LEXER_SET.code + "): T.g4:3:10: rule reference B is not currently supported in a set\n"
|
||||
};
|
||||
super.testErrors(pair, true);
|
||||
}
|
||||
|
||||
@Test public void testNotCharSetWithString() throws Exception {
|
||||
// might be a useful feature to add someday
|
||||
String[] pair = new String[] {
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println($A.text);} ;\n" +
|
||||
"A : ~('a'|'aa') ;\n" +
|
||||
"B : 'b' ;\n",
|
||||
"error(" + ErrorType.INVALID_LITERAL_IN_LEXER_SET.code + "): T.g4:3:10: multi-character literals are not allowed in lexer sets: 'aa'\n"
|
||||
};
|
||||
super.testErrors(pair, true);
|
||||
}
|
||||
|
||||
@Test public void testNotCharSetWithRuleRef3() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : A {System.out.println($A.text);} ;\n" +
|
||||
"A : ('a'|B) ;\n" + // this doesn't collapse to set but works
|
||||
"fragment\n" +
|
||||
"B : ~('a'|'c') ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "x", debug);
|
||||
assertEquals("x\n", found);
|
||||
}
|
||||
|
||||
@Test public void testCharSetLiteral() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : (A {System.out.println($A.text);})+ ;\n" +
|
||||
"A : [AaBb] ;\n" +
|
||||
"WS : (' '|'\\n')+ -> skip ;\n";
|
||||
String found = execParser("T.g4", grammar, "TParser", "TLexer",
|
||||
"a", "A a B b", debug);
|
||||
assertEquals("A\n" +
|
||||
"a\n" +
|
||||
"B\n" +
|
||||
"b\n", found);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
public class AbstractParserTestMethod extends TestMethod {
|
||||
|
||||
public String startRule;
|
||||
|
||||
public AbstractParserTestMethod(String name, String grammarName, String startRule) {
|
||||
super(name, grammarName, null, null, null, null);
|
||||
this.startRule = startRule;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
|
||||
public class CompositeLexerTestMethod extends LexerTestMethod {
|
||||
|
||||
public Grammar[] slaveGrammars;
|
||||
|
||||
public CompositeLexerTestMethod(String name, String grammarName,
|
||||
String input, String expectedOutput,
|
||||
String expectedErrors, String ... slaves) {
|
||||
super(name, grammarName, input, expectedOutput, expectedErrors, null);
|
||||
this.slaveGrammars = new Grammar[slaves.length];
|
||||
for(int i=0;i<slaves.length;i++)
|
||||
this.slaveGrammars[i] = new Grammar(name + "_" + slaves[i], slaves[i]);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGrammars(File grammarDir, String testFileName) throws Exception {
|
||||
for(Grammar slave : slaveGrammars)
|
||||
slave.load(new File(grammarDir, testFileName));
|
||||
super.loadGrammars(grammarDir, testFileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateGrammars(STGroup group) {
|
||||
for(Grammar slave : slaveGrammars)
|
||||
slave.generate(group);
|
||||
super.generateGrammars(group);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
|
||||
public class CompositeParserTestMethod extends ParserTestMethod {
|
||||
|
||||
public Grammar[] slaveGrammars;
|
||||
public boolean slaveIsLexer = false;
|
||||
|
||||
public CompositeParserTestMethod(String name, String grammarName,
|
||||
String startRule, String input, String expectedOutput,
|
||||
String expectedErrors, String ... slaves) {
|
||||
super(name, grammarName, startRule, input, expectedOutput, expectedErrors);
|
||||
this.slaveGrammars = new Grammar[slaves.length];
|
||||
for(int i=0;i<slaves.length;i++)
|
||||
this.slaveGrammars[i] = new Grammar(name + "_" + slaves[i], slaves[i]);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGrammars(File grammarDir, String testFileName) throws Exception {
|
||||
for(Grammar slave : slaveGrammars)
|
||||
slave.load(new File(grammarDir, testFileName));
|
||||
super.loadGrammars(grammarDir, testFileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateGrammars(STGroup group) {
|
||||
for(Grammar slave : slaveGrammars)
|
||||
slave.generate(group);
|
||||
super.generateGrammars(group);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
|
||||
public class ConcreteParserTestMethod extends TestMethod {
|
||||
|
||||
public String baseName;
|
||||
|
||||
public ConcreteParserTestMethod(String name, String input, String expectedOutput,
|
||||
String expectedErrors, Integer index) {
|
||||
super(name, null, input, expectedOutput, expectedErrors, index);
|
||||
this.baseName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGrammars(File grammarDir, String testFileName) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateGrammars(STGroup group) {
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,51 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.stringtemplate.v4.ST;
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
|
||||
public class Grammar {
|
||||
|
||||
public String fileName;
|
||||
public String grammarName;
|
||||
public String[] lines;
|
||||
public ST template;
|
||||
|
||||
public Grammar(String fileName, String grammarName) {
|
||||
this.fileName = fileName;
|
||||
this.grammarName = grammarName;
|
||||
}
|
||||
|
||||
public void load(File grammarDir) throws Exception {
|
||||
template = loadGrammar(grammarDir, fileName);
|
||||
}
|
||||
|
||||
protected ST loadGrammar(File grammarDir, String grammarFileName) throws Exception {
|
||||
File file = new File(grammarDir, grammarFileName + ".st");
|
||||
InputStream input = new FileInputStream(file);
|
||||
try {
|
||||
byte[] data = new byte[(int)file.length()];
|
||||
int next = 0;
|
||||
while(input.available()>0) {
|
||||
int read = input.read(data, next, data.length - next);
|
||||
next += read;
|
||||
}
|
||||
String s = new String(data);
|
||||
return new ST(s);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void generate(STGroup group) {
|
||||
template.add("grammarName", grammarName);
|
||||
template.groupThatCreatedThisInstance = group; // so templates get interpreted
|
||||
lines = template.render().split("\n");
|
||||
for(int i=0;i<lines.length;i++)
|
||||
lines[i] = Generator.escape(lines[i]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
public class LexerTestMethod extends TestMethod {
|
||||
|
||||
public String[] outputLines;
|
||||
public boolean lexerOnly = true;
|
||||
public boolean showDFA = false;
|
||||
|
||||
public LexerTestMethod(String name, String grammarName, String input,
|
||||
String expectedOutput, String expectedErrors, Integer index) {
|
||||
super(name, grammarName, input, expectedOutput, expectedErrors, index);
|
||||
outputLines = expectedOutput.split("\n");
|
||||
for(int i=0;i<outputLines.length;i++)
|
||||
outputLines[i] = Generator.escape(outputLines[i]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
public class ParserTestMethod extends TestMethod {
|
||||
|
||||
public String startRule;
|
||||
|
||||
public ParserTestMethod(String name, String grammarName, String startRule,
|
||||
String input, String expectedOutput, String expectedErrors) {
|
||||
super(name, grammarName, input, expectedOutput, expectedErrors, null);
|
||||
this.startRule = startRule;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.stringtemplate.v4.ST;
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
|
||||
public class TestFile {
|
||||
|
||||
List<TestMethod> unitTests = new ArrayList<TestMethod>();
|
||||
public String name;
|
||||
public List<String> tests = new ArrayList<String>();
|
||||
public boolean importErrorQueue = false;
|
||||
public boolean importGrammar = false;
|
||||
|
||||
public TestFile(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ParserTestMethod addParserTest(File grammarDir, String name, String grammarName, String methodName,
|
||||
String input, String expectedOutput, String expectedErrors) throws Exception {
|
||||
ParserTestMethod tm = new ParserTestMethod(name, grammarName, methodName, input, expectedOutput, expectedErrors);
|
||||
tm.loadGrammars(grammarDir, this.name);
|
||||
unitTests.add(tm);
|
||||
return tm;
|
||||
}
|
||||
|
||||
public AbstractParserTestMethod addParserTests(File grammarDir, String name, String grammarName, String methodName,
|
||||
String ... inputsAndOuputs) throws Exception {
|
||||
AbstractParserTestMethod tm = new AbstractParserTestMethod(name, grammarName, methodName);
|
||||
tm.loadGrammars(grammarDir, this.name);
|
||||
unitTests.add(tm);
|
||||
for(int i=0; i<inputsAndOuputs.length; i+=2) {
|
||||
ConcreteParserTestMethod cm = new ConcreteParserTestMethod(name,
|
||||
inputsAndOuputs[i], inputsAndOuputs[i+1], null,
|
||||
1 + (i/2));
|
||||
unitTests.add(cm);
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
|
||||
public AbstractParserTestMethod addParserTestsWithErrors(File grammarDir, String name, String grammarName, String methodName,
|
||||
String ... inputsOuputsAndErrors) throws Exception {
|
||||
AbstractParserTestMethod tm = new AbstractParserTestMethod(name, grammarName, methodName);
|
||||
tm.loadGrammars(grammarDir, this.name);
|
||||
unitTests.add(tm);
|
||||
for(int i=0; i<inputsOuputsAndErrors.length; i+=3) {
|
||||
ConcreteParserTestMethod cm = new ConcreteParserTestMethod(name,
|
||||
inputsOuputsAndErrors[i], inputsOuputsAndErrors[i+1], inputsOuputsAndErrors[i+2],
|
||||
1 + (i/3));
|
||||
unitTests.add(cm);
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
|
||||
public CompositeParserTestMethod addCompositeParserTest(File grammarDir, String name, String grammarName, String methodName,
|
||||
String input, String expectedOutput, String expectedErrors, String ... slaves) throws Exception {
|
||||
CompositeParserTestMethod tm = new CompositeParserTestMethod(name, grammarName, methodName, input, expectedOutput, expectedErrors, slaves);
|
||||
tm.loadGrammars(grammarDir, this.name);
|
||||
unitTests.add(tm);
|
||||
return tm;
|
||||
}
|
||||
|
||||
public LexerTestMethod addLexerTest(File grammarDir, String name, String grammarName,
|
||||
String input, String expectedOutput, String expectedErrors) throws Exception {
|
||||
return addLexerTest(grammarDir, name, grammarName, input, expectedOutput, expectedErrors, null);
|
||||
}
|
||||
|
||||
public LexerTestMethod addLexerTest(File grammarDir, String name, String grammarName,
|
||||
String input, String expectedOutput, String expectedErrors, Integer index) throws Exception {
|
||||
LexerTestMethod tm = new LexerTestMethod(name, grammarName, input, expectedOutput, expectedErrors, index);
|
||||
tm.loadGrammars(grammarDir, this.name);
|
||||
unitTests.add(tm);
|
||||
return tm;
|
||||
}
|
||||
|
||||
public CompositeLexerTestMethod addCompositeLexerTest(File grammarDir, String name, String grammarName,
|
||||
String input, String expectedOutput, String expectedErrors, String ... slaves) throws Exception {
|
||||
CompositeLexerTestMethod tm = new CompositeLexerTestMethod(name, grammarName, input, expectedOutput, expectedErrors, slaves);
|
||||
tm.loadGrammars(grammarDir, this.name);
|
||||
unitTests.add(tm);
|
||||
return tm;
|
||||
}
|
||||
|
||||
public void generateUnitTests(STGroup group) {
|
||||
for(TestMethod tm : unitTests) {
|
||||
tm.generateGrammars(group);
|
||||
String name = tm.getClass().getSimpleName();
|
||||
ST template = group.getInstanceOf(name);
|
||||
template.add("test", tm);
|
||||
tests.add(template.render());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.antlr.v4.test.rt.gen;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
|
||||
public abstract class TestMethod {
|
||||
|
||||
public String name;
|
||||
public Grammar grammar;
|
||||
public String afterGrammar;
|
||||
public String input;
|
||||
public String expectedOutput;
|
||||
public String expectedErrors;
|
||||
public boolean debug = false;
|
||||
|
||||
protected TestMethod(String name, String grammarName, String input,
|
||||
String expectedOutput, String expectedErrors, Integer index) {
|
||||
this.name = name + (index==null ? "" : "_" + index);
|
||||
this.grammar = new Grammar(name, grammarName);
|
||||
this.input = Generator.escape(input);
|
||||
this.expectedOutput = Generator.escape(expectedOutput);
|
||||
this.expectedErrors = Generator.escape(expectedErrors);
|
||||
}
|
||||
|
||||
public void loadGrammars(File grammarDir, String testFileName) throws Exception {
|
||||
grammar.load(new File(grammarDir, testFileName));
|
||||
}
|
||||
|
||||
public void generateGrammars(STGroup group) {
|
||||
grammar.generate(group);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
lexer grammar M;
|
||||
import S;
|
||||
B : 'b';
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,3 @@
|
|||
lexer grammar S;
|
||||
A : 'a' {<writeln("\"S.A\"")>};
|
||||
C : 'c' ;
|
|
@ -0,0 +1,4 @@
|
|||
lexer grammar M;
|
||||
import S;
|
||||
A : 'a' B {<writeln("\"M.A\"")>};
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,3 @@
|
|||
lexer grammar S;
|
||||
A : 'a' {<writeln("\"S.A\"")>};
|
||||
B : 'b' {<writeln("\"S.B\"")>};
|
|
@ -0,0 +1,4 @@
|
|||
grammar M;
|
||||
import S;
|
||||
s : a ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,2 @@
|
|||
parser grammar S;
|
||||
a : '=' 'a' {<write("\"S.a\"")>};
|
|
@ -0,0 +1,3 @@
|
|||
grammar M;
|
||||
import S;
|
||||
s : x INT;
|
|
@ -0,0 +1,5 @@
|
|||
parser grammar S;
|
||||
tokens { A, B, C }
|
||||
x : 'x' INT {<writeln("\"S.x\"")>};
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,17 @@
|
|||
// The lexer will create rules to match letters a, b, c.
|
||||
// The associated token types A, B, C must have the same value
|
||||
// and all import'd parsers. Since ANTLR regenerates all imports
|
||||
// for use with the delegator M, it can generate the same token type
|
||||
// mapping in each parser:
|
||||
// public static final int C=6;
|
||||
// public static final int EOF=-1;
|
||||
// public static final int B=5;
|
||||
// public static final int WS=7;
|
||||
// public static final int A=4;
|
||||
grammar M;
|
||||
import S,T;
|
||||
s : x y ; // matches AA, which should be 'aa'
|
||||
B : 'b' ; // another order: B, A, C
|
||||
A : 'a' ;
|
||||
C : 'c' ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,3 @@
|
|||
parser grammar S;
|
||||
tokens { A, B, C }
|
||||
x : A {<writeln("\"S.x\"")>};
|
|
@ -0,0 +1,3 @@
|
|||
parser grammar S;
|
||||
tokens { C, B, A } // reverse order
|
||||
y : A {<writeln("\"T.y\"")>};
|
|
@ -0,0 +1,4 @@
|
|||
grammar M; // uses no rules from the import
|
||||
import S;
|
||||
s : 'b'{<Invoke_foo()>}; // gS is import pointer
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,5 @@
|
|||
parser grammar S;
|
||||
@members {
|
||||
<Declare_foo()>
|
||||
}
|
||||
a : B;
|
|
@ -0,0 +1,5 @@
|
|||
grammar M;
|
||||
import S;
|
||||
s : a ;
|
||||
B : 'b' ; // defines B from inherited token space
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,5 @@
|
|||
grammar M;
|
||||
import S;
|
||||
s : label=a[3] {<writeln("$label.y")>} ;
|
||||
B : 'b' ; // defines B from inherited token space
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,2 @@
|
|||
parser grammar S;
|
||||
a[int x] returns [int y] : B {<write("\"S.a\"")>;$y=1000;};
|
|
@ -0,0 +1,5 @@
|
|||
grammar M;
|
||||
import S;
|
||||
s : a {<write("$a.text")>} ;
|
||||
B : 'b' ; // defines B from inherited token space
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,2 @@
|
|||
parser grammar S;
|
||||
a : B {<write("\"S.a\"")>};
|
|
@ -0,0 +1,2 @@
|
|||
parser grammar S;
|
||||
a : B {<writeln("\"S.a\"")>};
|
|
@ -0,0 +1,5 @@
|
|||
grammar M;
|
||||
import S,T;
|
||||
s : a ;
|
||||
B : 'b' ; // defines B from inherited token space
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,3 @@
|
|||
parser grammar S;
|
||||
a : B {<writeln("\"S.a\"")>};
|
||||
b : B;
|
|
@ -0,0 +1,2 @@
|
|||
parser grammar T;
|
||||
a : B {<writeln("\"T.a\"")>};
|
|
@ -0,0 +1,4 @@
|
|||
grammar M;
|
||||
import S;
|
||||
b : 'b'|'c';
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,3 @@
|
|||
parser grammar S;
|
||||
a : b {<write("\"S.a\"")>};
|
||||
b : B ;
|
|
@ -0,0 +1,4 @@
|
|||
grammar M;
|
||||
import S, T;
|
||||
b : 'b'|'c' {<writeln("\"M.b\"")>}|B|A;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,4 @@
|
|||
parser grammar S;
|
||||
a : b {<writeln("\"S.a\"")>};
|
||||
b : 'b' ;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
parser grammar S;
|
||||
tokens { A }
|
||||
b : 'b' {<writeln("\"T.b\"")>};
|
|
@ -0,0 +1,7 @@
|
|||
grammar M;
|
||||
import S;
|
||||
prog : decl ;
|
||||
type_ : 'int' | 'float' ;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip;
|
|
@ -0,0 +1,5 @@
|
|||
parser grammar S;
|
||||
type_ : 'int' ;
|
||||
decl : type_ ID ';'
|
||||
| type_ ID init ';' {<write("\"Decl: \" + $text")>};
|
||||
init : '=' INT;
|
|
@ -0,0 +1,4 @@
|
|||
grammar M;
|
||||
import S;
|
||||
program : 'test' 'test';
|
||||
WS : (UNICODE_CLASS_Zs)+ -> skip;
|
|
@ -0,0 +1,6 @@
|
|||
lexer grammar S;
|
||||
fragment
|
||||
UNICODE_CLASS_Zs : '\u0020' | '\u00A0' | '\u1680' | '\u180E'
|
||||
| '\u2000'..'\u200A'
|
||||
| '\u202F' | '\u205F' | '\u3000'
|
||||
;
|
|
@ -0,0 +1,5 @@
|
|||
grammar M;
|
||||
import S;
|
||||
s : a;
|
||||
B : 'b';
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,3 @@
|
|||
parser grammar S;
|
||||
options {}
|
||||
a : B;
|
|
@ -0,0 +1,5 @@
|
|||
grammar M;
|
||||
import S;
|
||||
s : a;
|
||||
B : 'b';
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,2 @@
|
|||
parser grammar S;
|
||||
a @after {} : B;
|
|
@ -0,0 +1,5 @@
|
|||
grammar M;
|
||||
import S;
|
||||
a : A {<Append("\"M.a: \"","$A"):writeln()>};
|
||||
A : 'abc' {<writeln("\"M.A\"")>};
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,2 @@
|
|||
lexer grammar S;
|
||||
ID : 'a'..'z'+;
|
|
@ -0,0 +1,5 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<DumpDFA()>}
|
||||
: ID | ID {} ;
|
||||
ID : 'a'..'z'+;
|
||||
WS : (' '|'\t'|'\n')+ -> skip ;
|
|
@ -0,0 +1,12 @@
|
|||
grammar <grammarName>;
|
||||
prog
|
||||
@init {<LL_EXACT_AMBIG_DETECTION()>}
|
||||
: expr expr {<writeln("\"alt 1\"")>}
|
||||
| expr
|
||||
;
|
||||
expr: '@'
|
||||
| ID '@'
|
||||
| ID
|
||||
;
|
||||
ID : [a-z]+ ;
|
||||
WS : [ \r\n\t]+ -> skip ;
|
|
@ -0,0 +1,9 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<DumpDFA()>}
|
||||
: '$' a | '@' b ;
|
||||
a : e ID ;
|
||||
b : e INT ID ;
|
||||
e : INT | ;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\t'|'\n')+ -> skip ;
|
|
@ -0,0 +1,9 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<DumpDFA()>}
|
||||
: ('$' a | '@' b)+ ;
|
||||
a : e ID ;
|
||||
b : e INT ID ;
|
||||
e : INT | ;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\t'|'\n')+ -> skip ;
|
|
@ -0,0 +1,13 @@
|
|||
grammar <grammarName>;
|
||||
s
|
||||
@init {<LL_EXACT_AMBIG_DETECTION()>}
|
||||
: expr[0] {<ToStringTree("$expr.ctx"):writeln()>};
|
||||
expr[int _p]
|
||||
: ID
|
||||
(
|
||||
{5 >= $_p}? '*' expr[6]
|
||||
| {4 >= $_p}? '+' expr[5]
|
||||
)*
|
||||
;
|
||||
ID : [a-zA-Z]+ ;
|
||||
WS : [ \r\n\t]+ -> skip ;
|
|
@ -0,0 +1,10 @@
|
|||
grammar <grammarName>;
|
||||
s
|
||||
@init {<LL_EXACT_AMBIG_DETECTION()>}
|
||||
@after {<DumpDFA()>}
|
||||
: '{' stat* '}' ;
|
||||
stat: 'if' ID 'then' stat ('else' ID)?
|
||||
| 'return'
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
WS : (' '|'\t'|'\n')+ -> skip ;
|
|
@ -0,0 +1,15 @@
|
|||
grammar <grammarName>;
|
||||
prog
|
||||
@init {<LL_EXACT_AMBIG_DETECTION()>}
|
||||
: expr_or_assign*;
|
||||
expr_or_assign
|
||||
: expr '++' {<writeln("\"fail.\"")>}
|
||||
| expr {<writeln("\"pass: \"+$expr.text")>}
|
||||
;
|
||||
expr: expr_primary ('\<-' ID)?;
|
||||
expr_primary
|
||||
: '(' ID ')'
|
||||
| ID '(' ID ')'
|
||||
| ID
|
||||
;
|
||||
ID : [a-z]+ ;
|
|
@ -0,0 +1,9 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<DumpDFA()>}
|
||||
: a;
|
||||
a : e ID ;
|
||||
b : e INT ID ;
|
||||
e : INT | ;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\t'|'\n')+ -> skip ;
|
|
@ -0,0 +1,21 @@
|
|||
grammar <grammarName>;
|
||||
prog: stat ;
|
||||
stat: expr NEWLINE # printExpr
|
||||
| ID '=' expr NEWLINE # assign
|
||||
| NEWLINE # blank
|
||||
;
|
||||
expr: expr ('*'|'/') expr # MulDiv
|
||||
| expr ('+'|'-') expr # AddSub
|
||||
| INT # int
|
||||
| ID # id
|
||||
| '(' expr ')' # parens
|
||||
;
|
||||
|
||||
MUL : '*' ; // assigns token name to '*' used above in grammar
|
||||
DIV : '/' ;
|
||||
ADD : '+' ;
|
||||
SUB : '-' ;
|
||||
ID : [a-zA-Z]+ ; // match identifiers
|
||||
INT : [0-9]+ ; // match integers
|
||||
NEWLINE:'\r'? '\n' ; // return newlines to parser (is end-statement signal)
|
||||
WS : [ \t]+ -> skip ; // toss out whitespace
|
|
@ -0,0 +1,14 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : declarator EOF ; // must indicate EOF can follow
|
||||
declarator
|
||||
: declarator '[' e ']'
|
||||
| declarator '[' ']'
|
||||
| declarator '(' ')'
|
||||
| '*' declarator // binds less tight than suffixes
|
||||
| '(' declarator ')'
|
||||
| ID
|
||||
;
|
||||
e : INT ;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,6 @@
|
|||
grammar <grammarName>;
|
||||
a @after {<ToStringTree("$ctx"):writeln()>} : a ID
|
||||
| ID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,13 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : e EOF ; // must indicate EOF can follow
|
||||
e : e '.' ID
|
||||
| e '.' 'this'
|
||||
| '-' e
|
||||
| e '*' e
|
||||
| e ('+'|'-') e
|
||||
| INT
|
||||
| ID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,56 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : e EOF ; // must indicate EOF can follow
|
||||
expressionList
|
||||
: e (',' e)*
|
||||
;
|
||||
e : '(' e ')'
|
||||
| 'this'
|
||||
| 'super'
|
||||
| INT
|
||||
| ID
|
||||
| type_ '.' 'class'
|
||||
| e '.' ID
|
||||
| e '.' 'this'
|
||||
| e '.' 'super' '(' expressionList? ')'
|
||||
| e '.' 'new' ID '(' expressionList? ')'
|
||||
| 'new' type_ ( '(' expressionList? ')' | ('[' e ']')+)
|
||||
| e '[' e ']'
|
||||
| '(' type_ ')' e
|
||||
| e ('++' | '--')
|
||||
| e '(' expressionList? ')'
|
||||
| ('+'|'-'|'++'|'--') e
|
||||
| ('~'|'!') e
|
||||
| e ('*'|'/'|'%') e
|
||||
| e ('+'|'-') e
|
||||
| e ('\<\<' | '>>>' | '>>') e
|
||||
| e ('\<=' | '>=' | '>' | '\<') e
|
||||
| e 'instanceof' e
|
||||
| e ('==' | '!=') e
|
||||
| e '&' e
|
||||
|\<assoc=right> e '^' e
|
||||
| e '|' e
|
||||
| e '&&' e
|
||||
| e '||' e
|
||||
| e '?' e ':' e
|
||||
|\<assoc=right>
|
||||
e ('='
|
||||
|'+='
|
||||
|'-='
|
||||
|'*='
|
||||
|'/='
|
||||
|'&='
|
||||
|'|='
|
||||
|'^='
|
||||
|'>>='
|
||||
|'>>>='
|
||||
|'\<\<='
|
||||
|'%=') e
|
||||
;
|
||||
type_: ID
|
||||
| ID '[' ']'
|
||||
| 'int'
|
||||
| 'int' '[' ']'
|
||||
;
|
||||
ID : ('a'..'z'|'A'..'Z'|'_'|'$')+;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,8 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : e;
|
||||
e : a=e op=('*'|'/') b=e {}
|
||||
| INT {}
|
||||
| '(' x=e ')' {}
|
||||
;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,8 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : e ;
|
||||
e : a=e op=('*'|'/') b=e {}{}
|
||||
| INT {}{}
|
||||
| '(' x=e ')' {}{}
|
||||
;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,9 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : e ;
|
||||
e : a=e op=('*'|'/') b=e {}{<True()>}?
|
||||
| a=e op=('+'|'-') b=e {}\<p=3>{<True()>}?\<fail='Message'>
|
||||
| INT {}{}
|
||||
| '(' x=e ')' {}{}
|
||||
;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,16 @@
|
|||
grammar <grammarName>;
|
||||
s : e {<writeln("$e.v")>};
|
||||
e returns [int v]
|
||||
: e '*' e {$v = <Cast("BinaryContext","$ctx")>.e(0).v * <Cast("BinaryContext","$ctx")>.e(1).v;} # binary
|
||||
| e '+' e {$v = <Cast("BinaryContext","$ctx")>.e(0).v + <Cast("BinaryContext","$ctx")>.e(1).v;} # binary
|
||||
| INT {$v = $INT.int;} # anInt
|
||||
| '(' e ')' {$v = $e.v;} # parens
|
||||
| left=e INC {<Cast("UnaryContext","$ctx"):Concat(".INC() != null"):Assert()>$v = $left.v + 1;} # unary
|
||||
| left=e DEC {<Cast("UnaryContext","$ctx"):Concat(".DEC() != null"):Assert()>$v = $left.v - 1;} # unary
|
||||
| ID {<AssignLocal("$v","3")>} # anID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
INC : '++' ;
|
||||
DEC : '--' ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,6 @@
|
|||
grammar <grammarName>;
|
||||
prog
|
||||
@after {<ToStringTree("$ctx"):writeln()>}
|
||||
: statement* EOF {};
|
||||
statement: letterA | statement letterA 'b' ;
|
||||
letterA: 'a';
|
|
@ -0,0 +1,11 @@
|
|||
grammar <grammarName>;
|
||||
s : e {<writeln("$e.result")>} ;
|
||||
e returns [String result]
|
||||
: ID '=' e1=e {$result = "(" + $ID.text + "=" + $e1.result + ")";}
|
||||
| ID {$result = $ID.text;}
|
||||
| e1=e '+' e2=e {$result = "(" + $e1.result + "+" + $e2.result + ")";}
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
grammar <grammarName>;
|
||||
s : e {<writeln("$e.v")>};
|
||||
e returns [int v, <StringList()> ignored]
|
||||
: a=e '*' b=e {$v = $a.v * $b.v;}
|
||||
| a=e '+' b=e {$v = $a.v + $b.v;}
|
||||
| INT {$v = $INT.int;}
|
||||
| '(' x=e ')' {$v = $x.v;}
|
||||
;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
grammar <grammarName>;
|
||||
s : q=e {<writeln("$e.v")>};
|
||||
e returns [int v]
|
||||
: a=e op='*' b=e {$v = $a.v * $b.v;} # mult
|
||||
| a=e '+' b=e {$v = $a.v + $b.v;} # add
|
||||
| INT {$v = $INT.int;} # anInt
|
||||
| '(' x=e ')' {$v = $x.v;} # parens
|
||||
| x=e '++' {$v = $x.v+1;} # inc
|
||||
| e '--' # dec
|
||||
| ID {$v = 3;} # anID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
INT : '0'..'9'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,13 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : expr EOF;
|
||||
expr:
|
||||
a=expr '*' a=expr #Factor
|
||||
| b+=expr (',' b+=expr)* '>>' c=expr #Send
|
||||
| ID #JustId //semantic check on modifiers
|
||||
;
|
||||
|
||||
ID : ('a'..'z'|'A'..'Z'|'_')
|
||||
('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
|
||||
;
|
||||
|
||||
WS : [ \t\n]+ -> skip ;
|
|
@ -0,0 +1,12 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : expr EOF;
|
||||
expr:
|
||||
a=expr '*' a=expr #Factor
|
||||
| b+=expr ',' b+=expr #Comma
|
||||
| b+=expr '>>' c=expr #Send
|
||||
| ID #JustId //semantic check on modifiers
|
||||
;
|
||||
ID : ('a'..'z'|'A'..'Z'|'_')
|
||||
('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
|
||||
;
|
||||
WS : [ \t\n]+ -> skip ;
|
|
@ -0,0 +1,7 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : a ;
|
||||
a : a {<True()>}? ID
|
||||
| ID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,7 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : a ;
|
||||
a : a ID {<False()>}?\<fail='custom message'>
|
||||
| ID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,7 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : a ;
|
||||
a : a ID
|
||||
| ID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,10 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : e EOF ; // must indicate EOF can follow or 'a\<EOF>' won't match
|
||||
e : e '*' e
|
||||
| e '+' e
|
||||
|\<assoc=right> e '?' e ':' e
|
||||
|\<assoc=right> e '=' e
|
||||
| ID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,10 @@
|
|||
grammar <grammarName>;
|
||||
s @after {<ToStringTree("$ctx"):writeln()>} : e EOF; // must indicate EOF can follow or 'a\<EOF>' won't match
|
||||
e :\<assoc=right> e '*' e
|
||||
|\<assoc=right> e '+' e
|
||||
|\<assoc=right> e '?' e ':' e
|
||||
|\<assoc=right> e '=' e
|
||||
| ID
|
||||
;
|
||||
ID : 'a'..'z'+ ;
|
||||
WS : (' '|'\n') -> skip ;
|
|
@ -0,0 +1,49 @@
|
|||
grammar <grammarName>;
|
||||
prog : expression EOF;
|
||||
expression
|
||||
: ID '(' expression (',' expression)* ')' # doFunction
|
||||
| '(' expression ')' # doParenthesis
|
||||
| '!' expression # doNot
|
||||
| '-' expression # doNegate
|
||||
| '+' expression # doPositiv
|
||||
| expression '^' expression # doPower
|
||||
| expression '*' expression # doMultipy
|
||||
| expression '/' expression # doDivide
|
||||
| expression '%' expression # doModulo
|
||||
| expression '-' expression # doMinus
|
||||
| expression '+' expression # doPlus
|
||||
| expression '=' expression # doEqual
|
||||
| expression '!=' expression # doNotEqual
|
||||
| expression '>' expression # doGreather
|
||||
| expression '>=' expression # doGreatherEqual
|
||||
| expression '\<' expression # doLesser
|
||||
| expression '\<=' expression # doLesserEqual
|
||||
| expression K_IN '(' expression (',' expression)* ')' # doIn
|
||||
| expression ( '&' | K_AND) expression # doAnd
|
||||
| expression ( '|' | K_OR) expression # doOr
|
||||
| '[' expression (',' expression)* ']' # newArray
|
||||
| K_TRUE # newTrueBoolean
|
||||
| K_FALSE # newFalseBoolean
|
||||
| NUMBER # newNumber
|
||||
| DATE # newDateTime
|
||||
| ID # newIdentifier
|
||||
| SQ_STRING # newString
|
||||
| K_NULL # newNull
|
||||
;
|
||||
|
||||
// Fragments
|
||||
fragment DIGIT : '0' .. '9';
|
||||
fragment UPPER : 'A' .. 'Z';
|
||||
fragment LOWER : 'a' .. 'z';
|
||||
fragment LETTER : LOWER | UPPER;
|
||||
fragment WORD : LETTER | '_' | '$' | '#' | '.';
|
||||
fragment ALPHANUM : WORD | DIGIT;
|
||||
|
||||
// Tokens
|
||||
ID : LETTER ALPHANUM*;
|
||||
NUMBER : DIGIT+ ('.' DIGIT+)? (('e'|'E')('+'|'-')? DIGIT+)?;
|
||||
DATE : '\'' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT (' ' DIGIT DIGIT ':' DIGIT DIGIT ':' DIGIT DIGIT ('.' DIGIT+)?)? '\'';
|
||||
SQ_STRING : '\'' ('\'\'' | ~'\'')* '\'';
|
||||
DQ_STRING : '\"' ('\\\"' | ~'\"')* '\"';
|
||||
WS : [ \t\n\r]+ -> skip ;
|
||||
COMMENTS : ('/*' .*? '*/' | '//' ~'\n'* '\n' ) -> skip;
|
|
@ -0,0 +1,3 @@
|
|||
lexer grammar <grammarName>;
|
||||
A : 'ab' ;
|
||||
B : 'abc' ;
|
|
@ -0,0 +1,4 @@
|
|||
lexer grammar <grammarName>;
|
||||
A : 'ab' ;
|
||||
B : 'abc' ;
|
||||
C : 'abcd' ;
|
|
@ -0,0 +1,3 @@
|
|||
lexer grammar <grammarName>;
|
||||
ACTION : '{' (ACTION | ~[{}])* '}';
|
||||
WS : [ \r\n\t]+ -> skip;
|
|
@ -0,0 +1,2 @@
|
|||
lexer grammar <grammarName>;
|
||||
A : 'abc' ;
|
|
@ -0,0 +1,2 @@
|
|||
lexer grammar <grammarName>;
|
||||
A : 'a' 'b' ;
|
|
@ -0,0 +1,2 @@
|
|||
lexer grammar <grammarName>;
|
||||
A : 'a' 'b' ;
|
|
@ -0,0 +1,2 @@
|
|||
lexer grammar <grammarName>;
|
||||
A : 'a' 'b' ;
|
|
@ -0,0 +1,2 @@
|
|||
lexer grammar <grammarName>;
|
||||
A : 'a' 'b' ;
|
|
@ -0,0 +1,5 @@
|
|||
grammar <grammarName>;
|
||||
start : ID ':' expr;
|
||||
expr : primary expr? {} | expr '->' ID;
|
||||
primary : ID;
|
||||
ID : [a-z]+;
|
|
@ -0,0 +1,4 @@
|
|||
lexer grammar <grammarName>;
|
||||
ACTION2 : '[' (STRING | ~'"')*? ']';
|
||||
STRING : '"' ('\\"' | .)*? '"';
|
||||
WS : [ \t\r\n]+ -> skip;
|
|
@ -0,0 +1,8 @@
|
|||
lexer grammar <grammarName>;
|
||||
I : ({<PlusText("stuff fail: "):writeln()>} 'a'
|
||||
| {<PlusText("stuff0: "):writeln()>}
|
||||
'a' {<PlusText("stuff1: "):writeln()>}
|
||||
'b' {<PlusText("stuff2: "):writeln()>})
|
||||
{<Text():writeln()>} ;
|
||||
WS : (' '|'\n') -> skip ;
|
||||
J : .;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue