/* [The "BSD licence"] Copyright (c) 2005-2006 Terence Parr All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.antlr.test; import org.antlr.tool.ErrorManager; import org.antlr.tool.GrammarSemanticsMessage; import org.antlr.tool.Message; import org.antlr.tool.Grammar; import org.antlr.Tool; import org.antlr.codegen.CodeGenerator; public class TestRewriteAST extends BaseTest { protected boolean debug = false; public void testDelete() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID INT -> ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc 34", debug); assertEquals("", found); } public void testSingleToken() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID -> ID;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("abc\n", found); } public void testSingleTokenToNewNode() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID -> ID[\"x\"];\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("x\n", found); } public void testSingleTokenToNewNodeRoot() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID -> ^(ID[\"x\"] INT);\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("(x INT)\n", found); } public void testSingleTokenToNewNode2() throws Exception { // Allow creation of new nodes w/o args. String grammar = "grammar TT;\n" + "options {output=AST;}\n" + "a : ID -> ID[ ];\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("TT.g", grammar, "TTParser", "TTLexer", "a", "abc", debug); assertEquals("ID\n", found); } public void testSingleCharLiteral() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'c' -> 'c';\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "c", debug); assertEquals("c\n", found); } public void testSingleStringLiteral() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'ick' -> 'ick';\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "ick", debug); assertEquals("ick\n", found); } public void testSingleRule() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : b -> b;\n" + "b : ID ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("abc\n", found); } public void testReorderTokens() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID INT -> INT ID;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc 34", debug); assertEquals("34 abc\n", found); } public void testReorderTokenAndRule() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : b INT -> INT b;\n" + "b : ID ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc 34", debug); assertEquals("34 abc\n", found); } public void testTokenTree() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID INT -> ^(INT ID);\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc 34", debug); assertEquals("(34 abc)\n", found); } public void testTokenTreeAfterOtherStuff() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'void' ID INT -> 'void' ^(INT ID);\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "void abc 34", debug); assertEquals("void (34 abc)\n", found); } public void testNestedTokenTreeWithOuterLoop() throws Exception { // verify that ID and INT both iterate over outer index variable String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {DUH;}\n" + "a : ID INT ID INT -> ^( DUH ID ^( DUH INT) )+ ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a 1 b 2", debug); assertEquals("(DUH a (DUH 1)) (DUH b (DUH 2))\n", found); } public void testOptionalSingleToken() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID -> ID? ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("abc\n", found); } public void testClosureSingleToken() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID ID -> ID* ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testPositiveClosureSingleToken() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID ID -> ID+ ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testOptionalSingleRule() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : b -> b?;\n" + "b : ID ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("abc\n", found); } public void testClosureSingleRule() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : b b -> b*;\n" + "b : ID ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testClosureOfLabel() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : x+=b x+=b -> $x*;\n" + "b : ID ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testOptionalLabelNoListLabel() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : (x=ID)? -> $x?;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a", debug); assertEquals("a\n", found); } public void testPositiveClosureSingleRule() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : b b -> b+;\n" + "b : ID ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testSinglePredicateT() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID -> {true}? ID -> ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("abc\n", found); } public void testSinglePredicateF() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID -> {false}? ID -> ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc", debug); assertEquals("", found); } public void testMultiplePredicate() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID INT -> {false}? ID\n" + " -> {true}? INT\n" + " -> \n" + " ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a 2", debug); assertEquals("2\n", found); } public void testMultiplePredicateTrees() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID INT -> {false}? ^(ID INT)\n" + " -> {true}? ^(INT ID)\n" + " -> ID\n" + " ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a 2", debug); assertEquals("(2 a)\n", found); } public void testSimpleTree() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : op INT -> ^(op INT);\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "-34", debug); assertEquals("(- 34)\n", found); } public void testSimpleTree2() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : op INT -> ^(INT op);\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "+ 34", debug); assertEquals("(34 +)\n", found); } public void testNestedTrees() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'var' (ID ':' type ';')+ -> ^('var' ^(':' ID type)+) ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "var a:int; b:float;", debug); assertEquals("(var (: a int) (: b float))\n", found); } public void testImaginaryTokenCopy() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {VAR;}\n" + "a : ID (',' ID)*-> ^(VAR ID)+ ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a,b,c", debug); assertEquals("(VAR a) (VAR b) (VAR c)\n", found); } public void testTokenUnreferencedOnLeftButDefined() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {VAR;}\n" + "a : b -> ID ;\n" + "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a", debug); assertEquals("ID\n", found); } public void testImaginaryTokenCopySetText() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {VAR;}\n" + "a : ID (',' ID)*-> ^(VAR[\"var\"] ID)+ ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a,b,c", debug); assertEquals("(var a) (var b) (var c)\n", found); } public void testImaginaryTokenNoCopyFromToken() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : lc='{' ID+ '}' -> ^(BLOCK[$lc] ID+) ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "{a b c}", debug); assertEquals("({ a b c)\n", found); } public void testImaginaryTokenNoCopyFromTokenSetText() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : lc='{' ID+ '}' -> ^(BLOCK[$lc,\"block\"] ID+) ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "{a b c}", debug); assertEquals("(block a b c)\n", found); } public void testMixedRewriteAndAutoAST() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : b b^ ;\n" + // 2nd b matches only an INT; can make it root "b : ID INT -> INT ID\n" + " | INT\n" + " ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a 1 2", debug); assertEquals("(2 1 a)\n", found); } public void testSubruleWithRewrite() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : b b ;\n" + "b : (ID INT -> INT ID | INT INT -> INT+ )\n" + " ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a 1 2 3", debug); assertEquals("1 a 2 3\n", found); } public void testSubruleWithRewrite2() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {TYPE;}\n" + "a : b b ;\n" + "b : 'int'\n" + " ( ID -> ^(TYPE 'int' ID)\n" + " | ID '=' INT -> ^(TYPE 'int' ID INT)\n" + " )\n" + " ';'\n" + " ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "int a; int b=3;", debug); assertEquals("(TYPE int a) (TYPE int b 3)\n", found); } public void testNestedRewriteShutsOffAutoAST() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : b b ;\n" + "b : ID ( ID (last=ID -> $last)+ ) ';'\n" + // get last ID " | INT\n" + // should still get auto AST construction " ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b c d; 42", debug); assertEquals("d 42\n", found); } public void testRewriteActions() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : atom -> ^({adaptor.create(INT,\"9\")} atom) ;\n" + "atom : INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "3", debug); assertEquals("(9 3)\n", found); } public void testRewriteActions2() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : atom -> {adaptor.create(INT,\"9\")} atom ;\n" + "atom : INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "3", debug); assertEquals("9 3\n", found); } public void testRefToOldValue() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : (atom -> atom) (op='+' r=atom -> ^($op $a $r) )* ;\n" + "atom : INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "3+4+5", debug); assertEquals("(+ (+ 3 4) 5)\n", found); } public void testCopySemanticsForRules() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : atom -> ^(atom atom) ;\n" + // NOT CYCLE! (dup atom) "atom : INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "3", debug); assertEquals("(3 3)\n", found); } public void testCopySemanticsForRules2() throws Exception { // copy type as a root for each invocation of (...)+ in rewrite String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : type ID (',' ID)* ';' -> ^(type ID)+ ;\n" + "type : 'int' ;\n" + "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "int a,b,c;", debug); assertEquals("(int a) (int b) (int c)\n", found); } public void testCopySemanticsForRules3() throws Exception { // copy type *and* modifier even though it's optional // for each invocation of (...)+ in rewrite String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : modifier? type ID (',' ID)* ';' -> ^(type modifier? ID)+ ;\n" + "type : 'int' ;\n" + "modifier : 'public' ;\n" + "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "public int a,b,c;", debug); assertEquals("(int public a) (int public b) (int public c)\n", found); } public void testCopySemanticsForRules3Double() throws Exception { // copy type *and* modifier even though it's optional // for each invocation of (...)+ in rewrite String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : modifier? type ID (',' ID)* ';' -> ^(type modifier? ID)+ ^(type modifier? ID)+ ;\n" + "type : 'int' ;\n" + "modifier : 'public' ;\n" + "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "public int a,b,c;", debug); assertEquals("(int public a) (int public b) (int public c) (int public a) (int public b) (int public c)\n", found); } public void testCopySemanticsForRules4() throws Exception { // copy type *and* modifier even though it's optional // for each invocation of (...)+ in rewrite String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {MOD;}\n" + "a : modifier? type ID (',' ID)* ';' -> ^(type ^(MOD modifier)? ID)+ ;\n" + "type : 'int' ;\n" + "modifier : 'public' ;\n" + "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "public int a,b,c;", debug); assertEquals("(int (MOD public) a) (int (MOD public) b) (int (MOD public) c)\n", found); } public void testCopySemanticsLists() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {MOD;}\n" + "a : ID (',' ID)* ';' -> ID+ ID+ ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a,b,c;", debug); assertEquals("a b c a b c\n", found); } public void testCopyRuleLabel() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x=b -> $x $x;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a", debug); assertEquals("a a\n", found); } public void testCopyRuleLabel2() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x=b -> ^($x $x);\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a", debug); assertEquals("(a a)\n", found); } public void testQueueingOfTokens() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'int' ID (',' ID)* ';' -> ^('int' ID+) ;\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "int a,b,c;", debug); assertEquals("(int a b c)\n", found); } public void testCopyOfTokens() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'int' ID ';' -> 'int' ID 'int' ID ;\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "int a;", debug); assertEquals("int a int a\n", found); } public void testTokenCopyInLoop() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'int' ID (',' ID)* ';' -> ^('int' ID)+ ;\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "int a,b,c;", debug); assertEquals("(int a) (int b) (int c)\n", found); } public void testTokenCopyInLoopAgainstTwoOthers() throws Exception { // must smear 'int' copies across as root of multiple trees String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : 'int' ID ':' INT (',' ID ':' INT)* ';' -> ^('int' ID INT)+ ;\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "int a:1,b:2,c:3;", debug); assertEquals("(int a 1) (int b 2) (int c 3)\n", found); } public void testListRefdOneAtATime() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID+ -> ID ID ID ;\n" + // works if 3 input IDs "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b c", debug); assertEquals("a b c\n", found); } public void testSplitListWithLabels() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {VAR;}\n"+ "a : first=ID others+=ID* -> $first VAR $others+ ;\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b c", debug); assertEquals("a VAR b c\n", found); } public void testComplicatedMelange() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : A A b=B B b=B c+=C C c+=C D {String s=$D.text;} -> A+ B+ C+ D ;\n" + "type : 'int' | 'float' ;\n" + "A : 'a' ;\n" + "B : 'b' ;\n" + "C : 'c' ;\n" + "D : 'd' ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a a b b b c c c d", debug); assertEquals("a a b b b c c c d\n", found); } public void testRuleLabel() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x=b -> $x;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a", debug); assertEquals("a\n", found); } public void testAmbiguousRule() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID a -> a | INT ;\n"+ "ID : 'a'..'z'+ ;\n" + "INT: '0'..'9'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc 34", debug); assertEquals("34\n", found); } public void testWeirdRuleRef() throws Exception { ErrorQueue equeue = new ErrorQueue(); ErrorManager.setErrorListener(equeue); String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID a -> $a | INT ;\n"+ "ID : 'a'..'z'+ ;\n" + "INT: '0'..'9'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; Grammar g = new Grammar(grammar); Tool antlr = newTool(); antlr.setOutputDirectory(null); // write to /dev/null CodeGenerator generator = new CodeGenerator(antlr, g, "Java"); g.setCodeGenerator(generator); generator.genRecognizer(); // $a is ambig; is it previous root or ref to a ref in alt? assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size()); } public void testRuleListLabel() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x+=b x+=b -> $x+;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testRuleListLabel2() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x+=b x+=b -> $x $x*;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testOptional() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x=b (y=b)? -> $x $y?;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a", debug); assertEquals("a\n", found); } public void testOptional2() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x=ID (y=b)? -> $x $y?;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testOptional3() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x=ID (y=b)? -> ($x $y)?;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testOptional4() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x+=ID (y=b)? -> ($x $y)?;\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("a b\n", found); } public void testOptional5() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : ID -> ID? ;\n"+ // match an ID to optional ID "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a", debug); assertEquals("a\n", found); } public void testArbitraryExprType() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : x+=b x+=b -> {new CommonTree()};\n"+ "b : ID ;\n"+ "ID : 'a'..'z'+ ;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); assertEquals("", found); } public void testSet() throws Exception { String grammar = "grammar T;\n" + "options { output = AST; } \n" + "a: (INT|ID)+ -> INT+ ID+ ;\n" + "INT: '0'..'9'+;\n" + "ID : 'a'..'z'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "2 a 34 de", debug); assertEquals("2 34 a de\n", found); } public void testSet2() throws Exception { String grammar = "grammar T;\n" + "options { output = AST; } \n" + "a: (INT|ID) -> INT? ID? ;\n" + "INT: '0'..'9'+;\n" + "ID : 'a'..'z'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "2", debug); assertEquals("2\n", found); } public void testSetWithLabel() throws Exception { // FAILS. The should probably generate a warning from antlr // See http://www.antlr.org:8888/browse/ANTLR-162 String grammar = "grammar T;\n" + "options { output = AST; } \n" + "a : x=(INT|ID) -> $x ;\n" + "INT: '0'..'9'+;\n" + "ID : 'a'..'z'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "2", debug); assertEquals("2\n", found); } public void testRewriteAction() throws Exception { String grammar = "grammar T; \n" + "options { output = AST; }\n" + "tokens { FLOAT; }\n" + "r\n" + " : INT -> {new CommonTree(new CommonToken(FLOAT,$INT.text+\".0\"))} \n" + " ; \n" + "INT : '0'..'9'+; \n" + "WS: (' ' | '\\n' | '\\t')+ {$channel = HIDDEN;}; \n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "r", "25", debug); assertEquals("25.0\n", found); } public void testOptionalSubruleWithoutRealElements() throws Exception { // copy type *and* modifier even though it's optional // for each invocation of (...)+ in rewrite String grammar = "grammar T;\n" + "options {output=AST;} \n" + "tokens {PARMS;} \n" + "\n" + "modulo \n" + " : 'modulo' ID ('(' parms+ ')')? -> ^('modulo' ID ^(PARMS parms+)?) \n" + " ; \n" + "parms : '#'|ID; \n" + "ID : ('a'..'z' | 'A'..'Z')+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "modulo", "modulo abc (x y #)", debug); assertEquals("(modulo abc (PARMS x y #))\n", found); } // C A R D I N A L I T Y I S S U E S public void testCardinality() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "tokens {BLOCK;}\n" + "a : ID ID INT INT INT -> (ID INT)+;\n"+ "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+; \n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; execParser("T.g", grammar, "TParser", "TLexer", "a", "a b 3 4 5", debug); String expecting = "org.antlr.runtime.tree.RewriteCardinalityException: token ID"; String found = getFirstLineOfException(); assertEquals(expecting, found); } public void testCardinality2() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID+ -> ID ID ID ;\n" + // only 2 input IDs "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; execParser("T.g", grammar, "TParser", "TLexer", "a", "a b", debug); String expecting = "org.antlr.runtime.tree.RewriteCardinalityException: token ID"; String found = getFirstLineOfException(); assertEquals(expecting, found); } public void testCardinality3() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID? INT -> ID INT ;\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; execParser("T.g", grammar, "TParser", "TLexer", "a", "3", debug); String expecting = "org.antlr.runtime.tree.RewriteEmptyStreamException: token ID"; String found = getFirstLineOfException(); assertEquals(expecting, found); } public void testLoopCardinality() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID? INT -> ID+ INT ;\n" + "op : '+'|'-' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; execParser("T.g", grammar, "TParser", "TLexer", "a", "3", debug); String expecting = "org.antlr.runtime.tree.RewriteEarlyExitException"; String found = getFirstLineOfException(); assertEquals(expecting, found); } public void testWildcard() throws Exception { String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : ID c=. -> $c;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abc 34", debug); assertEquals("34\n", found); } // E R R O R S public void testUnknownRule() throws Exception { ErrorQueue equeue = new ErrorQueue(); ErrorManager.setErrorListener(equeue); String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : INT -> ugh ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; Grammar g = new Grammar(grammar); Tool antlr = newTool(); antlr.setOutputDirectory(null); // write to /dev/null CodeGenerator generator = new CodeGenerator(antlr, g, "Java"); g.setCodeGenerator(generator); generator.genRecognizer(); int expectedMsgID = ErrorManager.MSG_UNDEFINED_RULE_REF; Object expectedArg = "ugh"; Object expectedArg2 = null; GrammarSemanticsMessage expectedMessage = new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); checkError(equeue, expectedMessage); } public void testKnownRuleButNotInLHS() throws Exception { ErrorQueue equeue = new ErrorQueue(); ErrorManager.setErrorListener(equeue); String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : INT -> b ;\n" + "b : 'b' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; Grammar g = new Grammar(grammar); Tool antlr = newTool(); antlr.setOutputDirectory(null); // write to /dev/null CodeGenerator generator = new CodeGenerator(antlr, g, "Java"); g.setCodeGenerator(generator); generator.genRecognizer(); int expectedMsgID = ErrorManager.MSG_REWRITE_ELEMENT_NOT_PRESENT_ON_LHS; Object expectedArg = "b"; Object expectedArg2 = null; GrammarSemanticsMessage expectedMessage = new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); checkError(equeue, expectedMessage); } public void testUnknownToken() throws Exception { ErrorQueue equeue = new ErrorQueue(); ErrorManager.setErrorListener(equeue); String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : INT -> ICK ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; Grammar g = new Grammar(grammar); Tool antlr = newTool(); antlr.setOutputDirectory(null); // write to /dev/null CodeGenerator generator = new CodeGenerator(antlr, g, "Java"); g.setCodeGenerator(generator); generator.genRecognizer(); int expectedMsgID = ErrorManager.MSG_UNDEFINED_TOKEN_REF_IN_REWRITE; Object expectedArg = "ICK"; Object expectedArg2 = null; GrammarSemanticsMessage expectedMessage = new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); checkError(equeue, expectedMessage); } public void testUnknownLabel() throws Exception { ErrorQueue equeue = new ErrorQueue(); ErrorManager.setErrorListener(equeue); String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : INT -> $foo ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; Grammar g = new Grammar(grammar); Tool antlr = newTool(); antlr.setOutputDirectory(null); // write to /dev/null CodeGenerator generator = new CodeGenerator(antlr, g, "Java"); g.setCodeGenerator(generator); generator.genRecognizer(); int expectedMsgID = ErrorManager.MSG_UNDEFINED_LABEL_REF_IN_REWRITE; Object expectedArg = "foo"; Object expectedArg2 = null; GrammarSemanticsMessage expectedMessage = new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); checkError(equeue, expectedMessage); } public void testUnknownCharLiteralToken() throws Exception { ErrorQueue equeue = new ErrorQueue(); ErrorManager.setErrorListener(equeue); String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : INT -> 'a' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; Grammar g = new Grammar(grammar); Tool antlr = newTool(); antlr.setOutputDirectory(null); // write to /dev/null CodeGenerator generator = new CodeGenerator(antlr, g, "Java"); g.setCodeGenerator(generator); generator.genRecognizer(); int expectedMsgID = ErrorManager.MSG_UNDEFINED_TOKEN_REF_IN_REWRITE; Object expectedArg = "'a'"; Object expectedArg2 = null; GrammarSemanticsMessage expectedMessage = new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); checkError(equeue, expectedMessage); } public void testUnknownStringLiteralToken() throws Exception { ErrorQueue equeue = new ErrorQueue(); ErrorManager.setErrorListener(equeue); String grammar = "grammar T;\n" + "options {output=AST;}\n" + "a : INT -> 'foo' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; Grammar g = new Grammar(grammar); Tool antlr = newTool(); antlr.setOutputDirectory(null); // write to /dev/null CodeGenerator generator = new CodeGenerator(antlr, g, "Java"); g.setCodeGenerator(generator); generator.genRecognizer(); int expectedMsgID = ErrorManager.MSG_UNDEFINED_TOKEN_REF_IN_REWRITE; Object expectedArg = "'foo'"; Object expectedArg2 = null; GrammarSemanticsMessage expectedMessage = new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); checkError(equeue, expectedMessage); } public void testExtraTokenInSimpleDecl() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "tokens {EXPR;}\n" + "decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "decl", "int 34 x=1;", debug); assertEquals("line 1:4 extraneous input '34' expecting ID\n", this.stderr); assertEquals("(EXPR int x 1)\n", found); // tree gets correct x and 1 tokens } public void testMissingIDInSimpleDecl() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "tokens {EXPR;}\n" + "decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "decl", "int =1;", debug); assertEquals("line 1:4 missing ID at '='\n", this.stderr); assertEquals("(EXPR int <missing ID> 1)\n", found); // tree gets invented ID token } public void testMissingSetInSimpleDecl() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "tokens {EXPR;}\n" + "decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;\n" + "type : 'int' | 'float' ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "decl", "x=1;", debug); assertEquals("line 1:0 mismatched input 'x' expecting set null\n", this.stderr); assertEquals("(EXPR <error: x> x 1)\n", found); // tree gets invented ID token } public void testMissingTokenGivesErrorNode() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "a : ID INT -> ID INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "a", "abc", debug); assertEquals("line 0:-1 missing INT at '<EOF>'\n", this.stderr); // doesn't do in-line recovery for sets (yet?) assertEquals("abc <missing INT>\n", found); } public void testExtraTokenGivesErrorNode() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "a : b c -> b c;\n" + "b : ID -> ID ;\n" + "c : INT -> INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "a", "abc ick 34", debug); assertEquals("line 1:4 extraneous input 'ick' expecting INT\n", this.stderr); assertEquals("abc 34\n", found); } public void testMissingFirstTokenGivesErrorNode() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "a : ID INT -> ID INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "a", "34", debug); assertEquals("line 1:0 missing ID at '34'\n", this.stderr); assertEquals("<missing ID> 34\n", found); } public void testMissingFirstTokenGivesErrorNode2() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "a : b c -> b c;\n" + "b : ID -> ID ;\n" + "c : INT -> INT ;\n" + "ID : 'a'..'z'+ ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "a", "34", debug); // finds an error at the first token, 34, and re-syncs. // re-synchronizing does not consume a token because 34 follows // ref to rule b (start of c). It then matches 34 in c. assertEquals("line 1:0 missing ID at '34'\n", this.stderr); assertEquals("<missing ID> 34\n", found); } public void testNoViableAltGivesErrorNode() throws Exception { String grammar = "grammar foo;\n" + "options {output=AST;}\n" + "a : b -> b | c -> c;\n" + "b : ID -> ID ;\n" + "c : INT -> INT ;\n" + "ID : 'a'..'z'+ ;\n" + "S : '*' ;\n" + "INT : '0'..'9'+;\n" + "WS : (' '|'\\n') {$channel=HIDDEN;} ;\n"; String found = execParser("foo.g", grammar, "fooParser", "fooLexer", "a", "*", debug); // finds an error at the first token, 34, and re-syncs. // re-synchronizing does not consume a token because 34 follows // ref to rule b (start of c). It then matches 34 in c. assertEquals("line 1:0 no viable alternative at input '*'\n", this.stderr); assertEquals("<unexpected: [@0,0:0='*',<6>,1:0], resync=*>\n", found); } // S U P P O R T protected void checkError(ErrorQueue equeue, GrammarSemanticsMessage expectedMessage) throws Exception { //System.out.println("errors="+equeue); Message foundMsg = null; for (int i = 0; i < equeue.errors.size(); i++) { Message m = (Message)equeue.errors.get(i); if (m.msgID==expectedMessage.msgID ) { foundMsg = m; } } assertTrue("no error; "+expectedMessage.msgID+" expected", equeue.errors.size()>0); assertTrue("too many errors; "+equeue.errors, equeue.errors.size()<=1); assertNotNull("couldn't find expected error: "+expectedMessage.msgID, foundMsg); assertTrue("error is not a GrammarSemanticsMessage", foundMsg instanceof GrammarSemanticsMessage); assertEquals(expectedMessage.arg, foundMsg.arg); assertEquals(expectedMessage.arg2, foundMsg.arg2); ErrorManager.resetErrorState(); // wack errors for next test } }