import unittest import textwrap import antlr3 import antlr3.tree import testbase import sys class TestAutoAST(testbase.ANTLRTest): def parserClass(self, base): class TParser(base): def __init__(self, *args, **kwargs): super().__init__(*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): super().__init__(*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.assertEqual(len(parser._errors), 0, parser._errors) result = "" if r: if hasattr(r, 'result'): result += r.result if r.tree: 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: return r.tree.toStringTree() return "" def testTokenList(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : ID INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN}; ''') found = self.execParser(grammar, "a", "abc 34") self.assertEqual("abc 34", found); def testTokenListInSingleAltBlock(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : (ID INT) ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar,"a", "abc 34") self.assertEqual("abc 34", found) def testSimpleRootAtOuterLevel(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : ID^ INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "abc 34") self.assertEqual("(abc 34)", found) def testSimpleRootAtOuterLevelReverse(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : INT ID^ ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "34 abc") self.assertEqual("(abc 34)", found) def testBang(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID INT! ID! INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "abc 34 dag 4532") self.assertEqual("abc 4532", found) def testOptionalThenRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ( ID INT )? ID^ ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a 1 b") self.assertEqual("(b a 1)", found) def testLabeledStringRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : v='void'^ ID ';' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "void foo;") self.assertEqual("(void foo ;)", found) def testWildcard(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : v='void'^ . ';' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "void foo;") self.assertEqual("(void foo ;)", found) def testWildcardRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : v='void' .^ ';' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "void foo;") self.assertEqual("(foo void ;)", found) def testWildcardRootWithLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : v='void' x=.^ ';' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "void foo;") self.assertEqual("(foo void ;)", found) def testWildcardRootWithListLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : v='void' x=.^ ';' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "void foo;") self.assertEqual("(foo void ;)", found) def testWildcardBangWithListLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : v='void' x=.! ';' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "void foo;") self.assertEqual("void ;", found) def testRootRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID^ INT^ ID ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a 34 c") self.assertEqual("(34 a c)", found) def testRootRoot2(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID INT^ ID^ ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a 34 c") self.assertEqual("(c (34 a))", found) def testRootThenRootInLoop(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID^ (INT '*'^ ID)+ ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a 34 * b 9 * c") self.assertEqual("(* (* (a 34) b 9) c)", found) def testNestedSubrule(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : 'void' (({pass}ID|INT) ID | 'null' ) ';' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "void a b;") self.assertEqual("void a b ;", found) def testInvokeRule(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : type ID ; type : {pass}'int' | 'float' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "int a") self.assertEqual("int a", found) def testInvokeRuleAsRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : type^ ID ; type : {pass}'int' | 'float' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "int a") self.assertEqual("(int a)", found) def testInvokeRuleAsRootWithLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : x=type^ ID ; type : {pass}'int' | 'float' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "int a") self.assertEqual("(int a)", found) def testInvokeRuleAsRootWithListLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : x+=type^ ID ; type : {pass}'int' | 'float' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "int a") self.assertEqual("(int a)", found) def testRuleRootInLoop(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID ('+'^ ID)* ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a+b+c+d") self.assertEqual("(+ (+ (+ a b) c) d)", found) def testRuleInvocationRuleRootInLoop(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID (op^ ID)* ; op : {pass}'+' | '-' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a+b+c-d") self.assertEqual("(- (+ (+ a b) c) d)", found) def testTailRecursion(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} s : a ; a : atom ('exp'^ a)? ; atom : INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "s", "3 exp 4 exp 5") self.assertEqual("(exp 3 (exp 4 5))", found) def testSet(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID|INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "abc") self.assertEqual("abc", found) def testSetRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ('+' | '-')^ ID ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "+abc") self.assertEqual("(+ abc)", found) @testbase.broken( "FAILS until antlr.g rebuilt in v3", testbase.GrammarCompileError) def testSetRootWithLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : x=('+' | '-')^ ID ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "+abc") self.assertEqual("(+ abc)", found) def testSetAsRuleRootInLoop(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ID (('+'|'-')^ ID)* ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a+b-c") self.assertEqual("(- (+ a b) c)", found) def testNotSet(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ~ID '+' INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "34+2") self.assertEqual("34 + 2", found) def testNotSetWithLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : x=~ID '+' INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "34+2") self.assertEqual("34 + 2", found) def testNotSetWithListLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : x=~ID '+' INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "34+2") self.assertEqual("34 + 2", found) def testNotSetRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ~'+'^ INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "34 55") self.assertEqual("(34 55)", found) def testNotSetRootWithLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ~'+'^ INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "34 55") self.assertEqual("(34 55)", found) def testNotSetRootWithListLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : ~'+'^ INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "34 55") self.assertEqual("(34 55)", found) def testNotSetRuleRootInLoop(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : INT (~INT^ INT)* ; blort : '+' ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "3+4+5") self.assertEqual("(+ (+ 3 4) 5)", found) @testbase.broken("FIXME: What happened to the semicolon?", AssertionError) def testTokenLabelReuse(self): # check for compilation problem due to multiple defines grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a returns [result] : id=ID id=ID {$result = "2nd id="+$id.text+";"} ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b") self.assertEqual("2nd id=b;a b", found) def testTokenLabelReuse2(self): # check for compilation problem due to multiple defines grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a returns [result]: id=ID id=ID^ {$result = "2nd id="+$id.text+','} ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b") self.assertEqual("2nd id=b,(b a)", found) def testTokenListLabelReuse(self): # check for compilation problem due to multiple defines # make sure ids has both ID tokens grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a returns [result] : ids+=ID ids+=ID {$result = "id list=[{}],".format(",".join([t.text for t in $ids]))} ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b") expecting = "id list=[a,b],a b" self.assertEqual(expecting, found) def testTokenListLabelReuse2(self): # check for compilation problem due to multiple defines # make sure ids has both ID tokens grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a returns [result] : ids+=ID^ ids+=ID {$result = "id list=[{}],".format(",".join([t.text for t in $ids]))} ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b") expecting = "id list=[a,b],(a b)" self.assertEqual(expecting, found) def testTokenListLabelRuleRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : id+=ID^ ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a") self.assertEqual("a", found) def testTokenListLabelBang(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : id+=ID! ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a") self.assertEqual("", found) def testRuleListLabel(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a returns [result]: x+=b x+=b { t=$x[1] $result = "2nd x="+t.toStringTree()+','; }; b : ID; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b") self.assertEqual("2nd x=b,a b", found) def testRuleListLabelRuleRoot(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a returns [result] : ( x+=b^ )+ { $result = "x="+$x[1].toStringTree()+','; } ; b : ID; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b") self.assertEqual("x=(b a),(b a)", found) def testRuleListLabelBang(self): grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a returns [result] : x+=b! x+=b { $result = "1st x="+$x[0].toStringTree()+','; } ; b : ID; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b") self.assertEqual("1st x=a,b", found) def testComplicatedMelange(self): # check for compilation problem grammar = textwrap.dedent( r''' grammar T; options {language=Python3;output=AST;} a : A b=B b=B c+=C c+=C D {s = $D.text} ; A : 'a' ; B : 'b' ; C : 'c' ; D : 'd' ; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "a b b c c d") self.assertEqual("a b b c c d", found) def testReturnValueWithAST(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a returns [result] : ID b { $result = str($b.i) + '\n';} ; b returns [i] : INT {$i=int($INT.text);} ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found = self.execParser(grammar, "a", "abc 34") self.assertEqual("34\nabc 34", found) def testSetLoop(self): grammar = textwrap.dedent( r''' grammar T; options { language=Python3;output=AST; } r : (INT|ID)+ ; ID : 'a'..'z' + ; INT : '0'..'9' +; WS: (' ' | '\n' | '\\t')+ {$channel = HIDDEN}; ''') found = self.execParser(grammar, "r", "abc 34 d") self.assertEqual("abc 34 d", found) def testExtraTokenInSimpleDecl(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} decl : 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.assertEqual(["line 1:4 extraneous input '34' expecting ID"], errors) self.assertEqual("(int x 1)", found) # tree gets correct x and 1 tokens def testMissingIDInSimpleDecl(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} tokens {EXPR;} decl : 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.assertEqual(["line 1:4 missing ID at '='"], errors) self.assertEqual("(int <missing ID> 1)", found) # tree gets invented ID token def testMissingSetInSimpleDecl(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} tokens {EXPR;} decl : 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.assertEqual(["line 1:0 mismatched input 'x' expecting set None"], errors) self.assertEqual("(<error: x> x 1)", found) # tree gets invented ID token def testMissingTokenGivesErrorNode(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : ID INT ; // follow is EOF ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found, errors = self.execParser(grammar, "a", "abc", expectErrors=True) self.assertEqual(["line 1:3 missing INT at '<EOF>'"], errors) self.assertEqual("abc <missing INT>", found) def testMissingTokenGivesErrorNodeInInvokedRule(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : b ; b : ID INT ; // follow should see EOF ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found, errors = self.execParser(grammar, "a", "abc", expectErrors=True) self.assertEqual(["line 1:3 mismatched input '<EOF>' expecting INT"], errors) self.assertEqual("<mismatched token: <EOF>, resync=abc>", found) def testExtraTokenGivesErrorNode(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : b c ; b : ID ; c : INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found, errors = self.execParser(grammar, "a", "abc ick 34", expectErrors=True) self.assertEqual(["line 1:4 extraneous input 'ick' expecting INT"], errors) self.assertEqual("abc 34", found) def testMissingFirstTokenGivesErrorNode(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : ID INT ; ID : 'a'..'z'+ ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found, errors = self.execParser(grammar, "a", "34", expectErrors=True) self.assertEqual(["line 1:0 missing ID at '34'"], errors) self.assertEqual("<missing ID> 34", found) def testMissingFirstTokenGivesErrorNode2(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : b c ; b : ID ; c : 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.assertEqual(["line 1:0 missing ID at '34'"], errors) self.assertEqual("<missing ID> 34", found) def testNoViableAltGivesErrorNode(self): grammar = textwrap.dedent( r''' grammar foo; options {language=Python3;output=AST;} a : b | c ; b : ID ; c : INT ; ID : 'a'..'z'+ ; S : '*' ; INT : '0'..'9'+; WS : (' '|'\n') {$channel=HIDDEN} ; ''') found, errors = self.execParser(grammar, "a", "*", expectErrors=True) self.assertEqual(["line 1:0 no viable alternative at input '*'"], errors) self.assertEqual("<unexpected: [@0,0:0='*',<S>,1:0], resync=*>", found) if __name__ == '__main__': unittest.main()