aboutsummaryrefslogtreecommitdiff
path: root/antlr-3.4/runtime/Python/tests/t058rewriteAST.py
diff options
context:
space:
mode:
Diffstat (limited to 'antlr-3.4/runtime/Python/tests/t058rewriteAST.py')
-rw-r--r--antlr-3.4/runtime/Python/tests/t058rewriteAST.py1517
1 files changed, 1517 insertions, 0 deletions
diff --git a/antlr-3.4/runtime/Python/tests/t058rewriteAST.py b/antlr-3.4/runtime/Python/tests/t058rewriteAST.py
new file mode 100644
index 0000000..15036f4
--- /dev/null
+++ b/antlr-3.4/runtime/Python/tests/t058rewriteAST.py
@@ -0,0 +1,1517 @@
+import unittest
+import textwrap
+import antlr3
+import antlr3.tree
+import testbase
+import sys
+
+class TestRewriteAST(testbase.ANTLRTest):
+ def parserClass(self, base):
+ class TParser(base):
+ def __init__(self, *args, **kwargs):
+ base.__init__(self, *args, **kwargs)
+
+ self._errors = []
+ self._output = ""
+
+
+ def capture(self, t):
+ self._output += t
+
+
+ def traceIn(self, ruleName, ruleIndex):
+ self.traces.append('>'+ruleName)
+
+
+ def traceOut(self, ruleName, ruleIndex):
+ self.traces.append('<'+ruleName)
+
+
+ def emitErrorMessage(self, msg):
+ self._errors.append(msg)
+
+
+ return TParser
+
+
+ def lexerClass(self, base):
+ class TLexer(base):
+ def __init__(self, *args, **kwargs):
+ base.__init__(self, *args, **kwargs)
+
+ self._output = ""
+
+
+ def capture(self, t):
+ self._output += t
+
+
+ def traceIn(self, ruleName, ruleIndex):
+ self.traces.append('>'+ruleName)
+
+
+ def traceOut(self, ruleName, ruleIndex):
+ self.traces.append('<'+ruleName)
+
+
+ def recover(self, input, re):
+ # no error recovery yet, just crash!
+ raise
+
+ return TLexer
+
+
+ def execParser(self, grammar, grammarEntry, input, expectErrors=False):
+ lexerCls, parserCls = self.compileInlineGrammar(grammar)
+
+ cStream = antlr3.StringStream(input)
+ lexer = lexerCls(cStream)
+ tStream = antlr3.CommonTokenStream(lexer)
+ parser = parserCls(tStream)
+ r = getattr(parser, grammarEntry)()
+
+ if not expectErrors:
+ self.assertEquals(len(parser._errors), 0, parser._errors)
+
+ result = ""
+
+ if r is not None:
+ if hasattr(r, 'result'):
+ result += r.result
+
+ if r.tree is not None:
+ result += r.tree.toStringTree()
+
+ if not expectErrors:
+ return result
+
+ else:
+ return result, parser._errors
+
+
+ def execTreeParser(self, grammar, grammarEntry, treeGrammar, treeEntry, input):
+ lexerCls, parserCls = self.compileInlineGrammar(grammar)
+ walkerCls = self.compileInlineGrammar(treeGrammar)
+
+ cStream = antlr3.StringStream(input)
+ lexer = lexerCls(cStream)
+ tStream = antlr3.CommonTokenStream(lexer)
+ parser = parserCls(tStream)
+ r = getattr(parser, grammarEntry)()
+ nodes = antlr3.tree.CommonTreeNodeStream(r.tree)
+ nodes.setTokenStream(tStream)
+ walker = walkerCls(nodes)
+ r = getattr(walker, treeEntry)()
+
+ if r is not None:
+ return r.tree.toStringTree()
+
+ return ""
+
+
+ def testDelete(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID INT -> ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc 34")
+ self.assertEquals("", found)
+
+
+ def testSingleToken(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID -> ID;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("abc", found)
+
+
+ def testSingleTokenToNewNode(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID -> ID["x"];
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("x", found)
+
+
+ def testSingleTokenToNewNodeRoot(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID -> ^(ID["x"] INT);
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("(x INT)", found)
+
+
+ def testSingleTokenToNewNode2(self):
+ # Allow creation of new nodes w/o args.
+ grammar = textwrap.dedent(
+ r'''
+ grammar TT;
+ options {language=Python;output=AST;}
+ a : ID -> ID[ ];
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("ID", found)
+
+
+ def testSingleCharLiteral(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'c' -> 'c';
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "c")
+ self.assertEquals("c", found)
+
+
+ def testSingleStringLiteral(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'ick' -> 'ick';
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "ick")
+ self.assertEquals("ick", found)
+
+
+ def testSingleRule(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : b -> b;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("abc", found)
+
+
+ def testReorderTokens(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID INT -> INT ID;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc 34")
+ self.assertEquals("34 abc", found)
+
+
+ def testReorderTokenAndRule(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : b INT -> INT b;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc 34")
+ self.assertEquals("34 abc", found)
+
+
+ def testTokenTree(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID INT -> ^(INT ID);
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc 34")
+ self.assertEquals("(34 abc)", found)
+
+
+ def testTokenTreeAfterOtherStuff(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'void' ID INT -> 'void' ^(INT ID);
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "void abc 34")
+ self.assertEquals("void (34 abc)", found)
+
+
+ def testNestedTokenTreeWithOuterLoop(self):
+ # verify that ID and INT both iterate over outer index variable
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {DUH;}
+ a : ID INT ID INT -> ^( DUH ID ^( DUH INT) )+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a 1 b 2")
+ self.assertEquals("(DUH a (DUH 1)) (DUH b (DUH 2))", found)
+
+
+ def testOptionalSingleToken(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID -> ID? ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("abc", found)
+
+
+ def testClosureSingleToken(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID ID -> ID* ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testPositiveClosureSingleToken(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID ID -> ID+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testOptionalSingleRule(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : b -> b?;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("abc", found)
+
+
+ def testClosureSingleRule(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : b b -> b*;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testClosureOfLabel(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : x+=b x+=b -> $x*;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testOptionalLabelNoListLabel(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : (x=ID)? -> $x?;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a")
+ self.assertEquals("a", found)
+
+
+ def testPositiveClosureSingleRule(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : b b -> b+;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testSinglePredicateT(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID -> {True}? ID -> ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("abc", found)
+
+
+ def testSinglePredicateF(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID -> {False}? ID -> ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc")
+ self.assertEquals("", found)
+
+
+ def testMultiplePredicate(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID INT -> {False}? ID
+ -> {True}? INT
+ ->
+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a 2")
+ self.assertEquals("2", found)
+
+
+ def testMultiplePredicateTrees(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID INT -> {False}? ^(ID INT)
+ -> {True}? ^(INT ID)
+ -> ID
+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a 2")
+ self.assertEquals("(2 a)", found)
+
+
+ def testSimpleTree(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : op INT -> ^(op INT);
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "-34")
+ self.assertEquals("(- 34)", found)
+
+
+ def testSimpleTree2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : op INT -> ^(INT op);
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "+ 34")
+ self.assertEquals("(34 +)", found)
+
+
+
+ def testNestedTrees(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'var' (ID ':' type ';')+ -> ^('var' ^(':' ID type)+) ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "var a:int; b:float;")
+ self.assertEquals("(var (: a int) (: b float))", found)
+
+
+ def testImaginaryTokenCopy(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {VAR;}
+ a : ID (',' ID)*-> ^(VAR ID)+ ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a,b,c")
+ self.assertEquals("(VAR a) (VAR b) (VAR c)", found)
+
+
+ def testTokenUnreferencedOnLeftButDefined(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {VAR;}
+ a : b -> ID ;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a")
+ self.assertEquals("ID", found)
+
+
+ def testImaginaryTokenCopySetText(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {VAR;}
+ a : ID (',' ID)*-> ^(VAR["var"] ID)+ ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a,b,c")
+ self.assertEquals("(var a) (var b) (var c)", found)
+
+
+ def testImaginaryTokenNoCopyFromToken(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : lc='{' ID+ '}' -> ^(BLOCK[$lc] ID+) ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "{a b c}")
+ self.assertEquals("({ a b c)", found)
+
+
+ def testImaginaryTokenNoCopyFromTokenSetText(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : lc='{' ID+ '}' -> ^(BLOCK[$lc,"block"] ID+) ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "{a b c}")
+ self.assertEquals("(block a b c)", found)
+
+
+ def testMixedRewriteAndAutoAST(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : b b^ ; // 2nd b matches only an INT; can make it root
+ b : ID INT -> INT ID
+ | INT
+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a 1 2")
+ self.assertEquals("(2 1 a)", found)
+
+
+ def testSubruleWithRewrite(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : b b ;
+ b : (ID INT -> INT ID | INT INT -> INT+ )
+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a 1 2 3")
+ self.assertEquals("1 a 2 3", found)
+
+
+ def testSubruleWithRewrite2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {TYPE;}
+ a : b b ;
+ b : 'int'
+ ( ID -> ^(TYPE 'int' ID)
+ | ID '=' INT -> ^(TYPE 'int' ID INT)
+ )
+ ';'
+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "int a; int b=3;")
+ self.assertEquals("(TYPE int a) (TYPE int b 3)", found)
+
+
+ def testNestedRewriteShutsOffAutoAST(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : b b ;
+ b : ID ( ID (last=ID -> $last)+ ) ';' // get last ID
+ | INT // should still get auto AST construction
+ ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b c d; 42")
+ self.assertEquals("d 42", found)
+
+
+ def testRewriteActions(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : atom -> ^({self.adaptor.create(INT,"9")} atom) ;
+ atom : INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "3")
+ self.assertEquals("(9 3)", found)
+
+
+ def testRewriteActions2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : atom -> {self.adaptor.create(INT,"9")} atom ;
+ atom : INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "3")
+ self.assertEquals("9 3", found)
+
+
+ def testRefToOldValue(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : (atom -> atom) (op='+' r=atom -> ^($op $a $r) )* ;
+ atom : INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "3+4+5")
+ self.assertEquals("(+ (+ 3 4) 5)", found)
+
+
+ def testCopySemanticsForRules(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : atom -> ^(atom atom) ; // NOT CYCLE! (dup atom)
+ atom : INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "3")
+ self.assertEquals("(3 3)", found)
+
+
+ def testCopySemanticsForRules2(self):
+ # copy type as a root for each invocation of (...)+ in rewrite
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : type ID (',' ID)* ';' -> ^(type ID)+ ;
+ type : 'int' ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "int a,b,c;")
+ self.assertEquals("(int a) (int b) (int c)", found)
+
+
+ def testCopySemanticsForRules3(self):
+ # copy type *and* modifier even though it's optional
+ # for each invocation of (...)+ in rewrite
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : modifier? type ID (',' ID)* ';' -> ^(type modifier? ID)+ ;
+ type : 'int' ;
+ modifier : 'public' ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "public int a,b,c;")
+ self.assertEquals("(int public a) (int public b) (int public c)", found)
+
+
+ def testCopySemanticsForRules3Double(self):
+ # copy type *and* modifier even though it's optional
+ # for each invocation of (...)+ in rewrite
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : modifier? type ID (',' ID)* ';' -> ^(type modifier? ID)+ ^(type modifier? ID)+ ;
+ type : 'int' ;
+ modifier : 'public' ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "public int a,b,c;")
+ self.assertEquals("(int public a) (int public b) (int public c) (int public a) (int public b) (int public c)", found)
+
+
+ def testCopySemanticsForRules4(self):
+ # copy type *and* modifier even though it's optional
+ # for each invocation of (...)+ in rewrite
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {MOD;}
+ a : modifier? type ID (',' ID)* ';' -> ^(type ^(MOD modifier)? ID)+ ;
+ type : 'int' ;
+ modifier : 'public' ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "public int a,b,c;")
+ self.assertEquals("(int (MOD public) a) (int (MOD public) b) (int (MOD public) c)", found)
+
+
+ def testCopySemanticsLists(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {MOD;}
+ a : ID (',' ID)* ';' -> ID+ ID+ ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a,b,c;")
+ self.assertEquals("a b c a b c", found)
+
+
+ def testCopyRuleLabel(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x=b -> $x $x;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a")
+ self.assertEquals("a a", found)
+
+
+ def testCopyRuleLabel2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x=b -> ^($x $x);
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a")
+ self.assertEquals("(a a)", found)
+
+
+ def testQueueingOfTokens(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'int' ID (',' ID)* ';' -> ^('int' ID+) ;
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "int a,b,c;")
+ self.assertEquals("(int a b c)", found)
+
+
+ def testCopyOfTokens(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'int' ID ';' -> 'int' ID 'int' ID ;
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "int a;")
+ self.assertEquals("int a int a", found)
+
+
+ def testTokenCopyInLoop(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'int' ID (',' ID)* ';' -> ^('int' ID)+ ;
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "int a,b,c;")
+ self.assertEquals("(int a) (int b) (int c)", found)
+
+
+ def testTokenCopyInLoopAgainstTwoOthers(self):
+ # must smear 'int' copies across as root of multiple trees
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : 'int' ID ':' INT (',' ID ':' INT)* ';' -> ^('int' ID INT)+ ;
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "int a:1,b:2,c:3;")
+ self.assertEquals("(int a 1) (int b 2) (int c 3)", found)
+
+
+ def testListRefdOneAtATime(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID+ -> ID ID ID ; // works if 3 input IDs
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b c")
+ self.assertEquals("a b c", found)
+
+
+ def testSplitListWithLabels(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {VAR;}
+ a : first=ID others+=ID* -> $first VAR $others+ ;
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b c")
+ self.assertEquals("a VAR b c", found)
+
+
+ def testComplicatedMelange(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : A A b=B B b=B c+=C C c+=C D {s=$D.text} -> A+ B+ C+ D ;
+ type : 'int' | 'float' ;
+ A : 'a' ;
+ B : 'b' ;
+ C : 'c' ;
+ D : 'd' ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a a b b b c c c d")
+ self.assertEquals("a a b b b c c c d", found)
+
+
+ def testRuleLabel(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x=b -> $x;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a")
+ self.assertEquals("a", found)
+
+
+ def testAmbiguousRule(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID a -> a | INT ;
+ ID : 'a'..'z'+ ;
+ INT: '0'..'9'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar,
+ "a", "abc 34")
+ self.assertEquals("34", found)
+
+
+ def testRuleListLabel(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x+=b x+=b -> $x+;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testRuleListLabel2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x+=b x+=b -> $x $x*;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testOptional(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x=b (y=b)? -> $x $y?;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a")
+ self.assertEquals("a", found)
+
+
+ def testOptional2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x=ID (y=b)? -> $x $y?;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testOptional3(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x=ID (y=b)? -> ($x $y)?;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testOptional4(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x+=ID (y=b)? -> ($x $y)?;
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("a b", found)
+
+
+ def testOptional5(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : ID -> ID? ; // match an ID to optional ID
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a")
+ self.assertEquals("a", found)
+
+
+ def testArbitraryExprType(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : x+=b x+=b -> {CommonTree(None)};
+ b : ID ;
+ ID : 'a'..'z'+ ;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "a b")
+ self.assertEquals("", found)
+
+
+ def testSet(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a: (INT|ID)+ -> INT+ ID+ ;
+ INT: '0'..'9'+;
+ ID : 'a'..'z'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "2 a 34 de")
+ self.assertEquals("2 34 a de", found)
+
+
+ def testSet2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a: (INT|ID) -> INT? ID? ;
+ INT: '0'..'9'+;
+ ID : 'a'..'z'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "2")
+ self.assertEquals("2", found)
+
+
+ @testbase.broken("http://www.antlr.org:8888/browse/ANTLR-162",
+ antlr3.tree.RewriteEmptyStreamException)
+ def testSetWithLabel(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : x=(INT|ID) -> $x ;
+ INT: '0'..'9'+;
+ ID : 'a'..'z'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "2")
+ self.assertEquals("2", found)
+
+
+ def testRewriteAction(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens { FLOAT; }
+ r
+ : INT -> {CommonTree(CommonToken(type=FLOAT, text=$INT.text+".0"))}
+ ;
+ INT : '0'..'9'+;
+ WS: (' ' | '\n' | '\t')+ {$channel = HIDDEN;};
+ ''')
+
+ found = self.execParser(grammar, "r", "25")
+ self.assertEquals("25.0", found)
+
+
+ def testOptionalSubruleWithoutRealElements(self):
+ # copy type *and* modifier even though it's optional
+ # for each invocation of (...)+ in rewrite
+ grammar = textwrap.dedent(
+ r"""
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {PARMS;}
+
+ modulo
+ : 'modulo' ID ('(' parms+ ')')? -> ^('modulo' ID ^(PARMS parms+)?)
+ ;
+ parms : '#'|ID;
+ ID : ('a'..'z' | 'A'..'Z')+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ """)
+
+ found = self.execParser(grammar, "modulo", "modulo abc (x y #)")
+ self.assertEquals("(modulo abc (PARMS x y #))", found)
+
+
+ ## C A R D I N A L I T Y I S S U E S
+
+ def testCardinality(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ tokens {BLOCK;}
+ a : ID ID INT INT INT -> (ID INT)+;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ try:
+ self.execParser(grammar, "a", "a b 3 4 5")
+ self.fail()
+ except antlr3.tree.RewriteCardinalityException:
+ pass
+
+
+ def testCardinality2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID+ -> ID ID ID ; // only 2 input IDs
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ try:
+ self.execParser(grammar, "a", "a b")
+ self.fail()
+ except antlr3.tree.RewriteCardinalityException:
+ pass
+
+
+ def testCardinality3(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID? INT -> ID INT ;
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ try:
+ self.execParser(grammar, "a", "3")
+ self.fail()
+ except antlr3.tree.RewriteEmptyStreamException:
+ pass
+
+
+ def testLoopCardinality(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID? INT -> ID+ INT ;
+ op : '+'|'-' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ try:
+ self.execParser(grammar, "a", "3")
+ self.fail()
+ except antlr3.tree.RewriteEarlyExitException:
+ pass
+
+
+ def testWildcard(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar T;
+ options {language=Python;output=AST;}
+ a : ID c=. -> $c;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found = self.execParser(grammar, "a", "abc 34")
+ self.assertEquals("34", found)
+
+
+ # E R R O R S
+
+ def testExtraTokenInSimpleDecl(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ tokens {EXPR;}
+ decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "decl", "int 34 x=1;",
+ expectErrors=True)
+ self.assertEquals(["line 1:4 extraneous input u'34' expecting ID"],
+ errors)
+ self.assertEquals("(EXPR int x 1)", found) # tree gets correct x and 1 tokens
+
+
+ #@testbase.broken("FIXME", AssertionError)
+ def testMissingIDInSimpleDecl(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ tokens {EXPR;}
+ decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "decl", "int =1;",
+ expectErrors=True)
+ self.assertEquals(["line 1:4 missing ID at u'='"], errors)
+ self.assertEquals("(EXPR int <missing ID> 1)", found) # tree gets invented ID token
+
+
+ def testMissingSetInSimpleDecl(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ tokens {EXPR;}
+ decl : type ID '=' INT ';' -> ^(EXPR type ID INT) ;
+ type : 'int' | 'float' ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "decl", "x=1;",
+ expectErrors=True)
+ self.assertEquals(["line 1:0 mismatched input u'x' expecting set None"],
+ errors);
+ self.assertEquals("(EXPR <error: x> x 1)", found) # tree gets invented ID token
+
+
+ def testMissingTokenGivesErrorNode(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ a : ID INT -> ID INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "a", "abc",
+ expectErrors=True)
+ self.assertEquals(["line 1:3 missing INT at '<EOF>'"], errors)
+ # doesn't do in-line recovery for sets (yet?)
+ self.assertEquals("abc <missing INT>", found)
+
+
+ def testExtraTokenGivesErrorNode(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ a : b c -> b c;
+ b : ID -> ID ;
+ c : INT -> INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "a", "abc ick 34",
+ expectErrors=True)
+ self.assertEquals(["line 1:4 extraneous input u'ick' expecting INT"],
+ errors)
+ self.assertEquals("abc 34", found)
+
+
+ #@testbase.broken("FIXME", AssertionError)
+ def testMissingFirstTokenGivesErrorNode(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ a : ID INT -> ID INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "a", "34", expectErrors=True)
+ self.assertEquals(["line 1:0 missing ID at u'34'"], errors)
+ self.assertEquals("<missing ID> 34", found)
+
+
+ #@testbase.broken("FIXME", AssertionError)
+ def testMissingFirstTokenGivesErrorNode2(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ a : b c -> b c;
+ b : ID -> ID ;
+ c : INT -> INT ;
+ ID : 'a'..'z'+ ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "a", "34", expectErrors=True)
+ # 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.
+ self.assertEquals(["line 1:0 missing ID at u'34'"], errors)
+ self.assertEquals("<missing ID> 34", found)
+
+
+ def testNoViableAltGivesErrorNode(self):
+ grammar = textwrap.dedent(
+ r'''
+ grammar foo;
+ options {language=Python;output=AST;}
+ a : b -> b | c -> c;
+ b : ID -> ID ;
+ c : INT -> INT ;
+ ID : 'a'..'z'+ ;
+ S : '*' ;
+ INT : '0'..'9'+;
+ WS : (' '|'\n') {$channel=HIDDEN;} ;
+ ''')
+
+ found, errors = self.execParser(grammar, "a", "*", expectErrors=True)
+ # 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.
+ self.assertEquals(["line 1:0 no viable alternative at input u'*'"],
+ errors);
+ self.assertEquals("<unexpected: [@0,0:0=u'*',<6>,1:0], resync=*>",
+ found)
+
+
+if __name__ == '__main__':
+ unittest.main()