blob: a01b66a836fe981fb0bf34885be3fae0e189d6c4 [file] [log] [blame]
package org.antlr.test;
import org.junit.Test;
/** */
public class TestLeftRecursion extends BaseTest {
protected boolean debug = false;
@Test public void testSimple() throws Exception {
String grammar =
"grammar T;\n" +
"s : a {System.out.println($a.text);} ;\n" +
"a : a ID\n" +
" | ID" +
" ;\n" +
"ID : 'a'..'z'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String found = execParser("T.g", grammar, "TParser", "TLexer",
"s", "a b c", debug);
String expecting = "abc\n";
assertEquals(expecting, found);
}
@Test public void testSemPred() throws Exception {
String grammar =
"grammar T;\n" +
"s : a {System.out.println($a.text);} ;\n" +
"a : a {true}? ID\n" +
" | ID" +
" ;\n" +
"ID : 'a'..'z'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String found = execParser("T.g", grammar, "TParser", "TLexer",
"s", "a b c", debug);
String expecting = "abc\n";
assertEquals(expecting, found);
}
@Test public void testTernaryExpr() throws Exception {
String grammar =
"grammar T;\n" +
"options {output=AST;}\n" +
"e : e '*'^ e" +
" | e '+'^ e" +
" | e '?'<assoc=right>^ e ':'! e" +
" | e '='<assoc=right>^ e" +
" | ID" +
" ;\n" +
"ID : 'a'..'z'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String[] tests = {
"a", "a",
"a+b", "(+ a b)",
"a*b", "(* a b)",
"a?b:c", "(? a b c)",
"a=b=c", "(= a (= b c))",
"a?b+c:d", "(? a (+ b c) d)",
"a?b=c:d", "(? a (= b c) d)",
"a? b?c:d : e", "(? a (? b c d) e)",
"a?b: c?d:e", "(? a b (? c d e))",
};
runTests(grammar, tests, "e");
}
@Test public void testDeclarationsUsingASTOperators() throws Exception {
String grammar =
"grammar T;\n" +
"options {output=AST;}\n" +
"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", "a",
"*a", "(* a)",
"**a", "(* (* a))",
"a[3]", "([ a 3)",
"b[]", "([ b)",
"(a)", "a",
"a[]()", "(( ([ a))",
"a[][]", "([ ([ a))",
"*a[]", "(* ([ a))",
"(*a)[]", "([ (* a))",
};
runTests(grammar, tests, "declarator");
}
@Test public void testDeclarationsUsingRewriteOperators() throws Exception {
String grammar =
"grammar T;\n" +
"options {output=AST;}\n" +
"declarator\n" +
" : declarator '[' e ']' -> ^('[' declarator e)\n" +
" | declarator '[' ']' -> ^('[' declarator)\n" +
" | declarator '(' ')' -> ^('(' declarator)\n" +
" | '*' declarator -> ^('*' declarator) \n" + // binds less tight than suffixes
" | '(' declarator ')' -> declarator\n" +
" | ID -> ID\n" +
" ;\n" +
"e : INT ;\n" +
"ID : 'a'..'z'+ ;\n" +
"INT : '0'..'9'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String[] tests = {
"a", "a",
"*a", "(* a)",
"**a", "(* (* a))",
"a[3]", "([ a 3)",
"b[]", "([ b)",
"(a)", "a",
"a[]()", "(( ([ a))",
"a[][]", "([ ([ a))",
"*a[]", "(* ([ a))",
"(*a)[]", "([ (* a))",
};
runTests(grammar, tests, "declarator");
}
@Test public void testExpressionsUsingASTOperators() throws Exception {
String grammar =
"grammar T;\n" +
"options {output=AST;}\n" +
"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", "a",
"1", "1",
"a+1", "(+ a 1)",
"a*1", "(* a 1)",
"a.b", "(. a b)",
"a.this", "(. a this)",
"a-b+c", "(+ (- a b) c)",
"a+b*c", "(+ a (* b c))",
"a.b+1", "(+ (. a b) 1)",
"-a", "(- a)",
"-a+b", "(+ (- a) b)",
"-a.b", "(- (. a b))",
};
runTests(grammar, tests, "e");
}
@Test public void testExpressionsUsingRewriteOperators() throws Exception {
String grammar =
"grammar T;\n" +
"options {output=AST;}\n" +
"e : e '.' ID -> ^('.' e ID)\n" +
" | e '.' 'this' -> ^('.' e 'this')\n" +
" | '-' e -> ^('-' e)\n" +
" | e '*' b=e -> ^('*' e $b)\n" +
" | e (op='+'|op='-') b=e -> ^($op e $b)\n" +
" | INT -> INT\n" +
" | ID -> ID\n" +
" ;\n" +
"ID : 'a'..'z'+ ;\n" +
"INT : '0'..'9'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String[] tests = {
"a", "a",
"1", "1",
"a+1", "(+ a 1)",
"a*1", "(* a 1)",
"a.b", "(. a b)",
"a.this", "(. a this)",
"a+b*c", "(+ a (* b c))",
"a.b+1", "(+ (. a b) 1)",
"-a", "(- a)",
"-a+b", "(+ (- a) b)",
"-a.b", "(- (. a b))",
};
runTests(grammar, tests, "e");
}
@Test public void testExpressionAssociativity() throws Exception {
String grammar =
"grammar T;\n" +
"options {output=AST;}\n" +
"e\n" +
" : e '.'^ ID\n" +
" | '-'^ e\n" +
" | e '^'<assoc=right>^ e\n" +
" | e '*'^ e\n" +
" | e ('+'^|'-'^) e\n" +
" | e ('='<assoc=right>^ |'+='<assoc=right>^) e\n" +
" | INT\n" +
" | ID\n" +
" ;\n" +
"ID : 'a'..'z'+ ;\n" +
"INT : '0'..'9'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String[] tests = {
"a", "a",
"1", "1",
"a+1", "(+ a 1)",
"a*1", "(* a 1)",
"a.b", "(. a b)",
"a-b+c", "(+ (- a b) c)",
"a+b*c", "(+ a (* b c))",
"a.b+1", "(+ (. a b) 1)",
"-a", "(- a)",
"-a+b", "(+ (- a) b)",
"-a.b", "(- (. a b))",
"a^b^c", "(^ a (^ b c))",
"a=b=c", "(= a (= b c))",
"a=b=c+d.e","(= a (= b (+ c (. d e))))",
};
runTests(grammar, tests, "e");
}
@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" +
"options {output=AST;}\n" +
"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? ')'! | (options {k=1;}:'[' e ']'!)+)\n" + // ugly; simplified
" | 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" +
" | e '^'<assoc=right>^ e\n" +
" | e '|'^ e\n" +
" | e '&&'^ e\n" +
" | e '||'^ e\n" +
" | e '?' e ':' e\n" +
" | e ('='<assoc=right>^\n" +
" |'+='<assoc=right>^\n" +
" |'-='<assoc=right>^\n" +
" |'*='<assoc=right>^\n" +
" |'/='<assoc=right>^\n" +
" |'&='<assoc=right>^\n" +
" |'|='<assoc=right>^\n" +
" |'^='<assoc=right>^\n" +
" |'>>='<assoc=right>^\n" +
" |'>>>='<assoc=right>^\n" +
" |'<<='<assoc=right>^\n" +
" |'%='<assoc=right>^) 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", "a",
"1", "1",
"a+1", "(+ a 1)",
"a*1", "(* a 1)",
"a.b", "(. a b)",
"a-b+c", "(+ (- a b) c)",
"a+b*c", "(+ a (* b c))",
"a.b+1", "(+ (. a b) 1)",
"-a", "(- a)",
"-a+b", "(+ (- a) b)",
"-a.b", "(- (. a b))",
"a^b^c", "(^ a (^ b c))",
"a=b=c", "(= a (= b c))",
"a=b=c+d.e","(= a (= b (+ c (. d e))))",
"a|b&c", "(| a (& b c))",
"(a|b)&c", "(& (| a b) c)",
"a > b", "(> a b)",
"a >> b", "(> a b)", // text is from one token
"a < b", "(< a b)",
"(T)x", "(( T x)",
"new A().b", "(. (new A () b)",
"(T)t.f()", "(( (( T (. t f)))",
"a.f(x)==T.c", "(== (( (. a f) x) (. T c))",
"a.f().g(x,1)", "(( (. (( (. a f)) g) x 1)",
"new T[((n-1) * x) + 1]", "(new T [ (+ (* (- n 1) x) 1))",
};
runTests(grammar, tests, "e");
}
@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" +
" : e '*' b=e {$v *= $b.v;}\n" +
" | e '+' b=e {$v += $b.v;}\n" +
" | INT {$v = $INT.int;}\n" +
" ;\n" +
"INT : '0'..'9'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String[] tests = {
"4", "4",
"1+2", "3",
};
runTests(grammar, tests, "s");
}
@Test public void testReturnValueAndActionsAndASTs() throws Exception {
String grammar =
"grammar T;\n" +
"options {output=AST;}\n" +
"s : e {System.out.print(\"v=\"+$e.v+\", \");} ;\n" +
"e returns [int v, List<String> ignored]\n" +
" : e '*'^ b=e {$v *= $b.v;}\n" +
" | e '+'^ b=e {$v += $b.v;}\n" +
" | INT {$v = $INT.int;}\n" +
" ;\n" +
"INT : '0'..'9'+ ;\n" +
"WS : (' '|'\\n') {skip();} ;\n";
String[] tests = {
"4", "v=4, 4",
"1+2", "v=3, (+ 1 2)",
};
runTests(grammar, tests, "s");
}
public void runTests(String grammar, String[] tests, String startRule) {
rawGenerateAndBuildRecognizer("T.g", grammar, "TParser", "TLexer", debug);
boolean parserBuildsTrees =
grammar.indexOf("output=AST")>=0 ||
grammar.indexOf("output = AST")>=0;
writeRecognizerAndCompile("TParser",
null,
"TLexer",
startRule,
null,
parserBuildsTrees,
false,
false,
debug);
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);
}
}
}