• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import textwrap
3import antlr3
4import antlr3.tree
5import antlr3.debug
6import testbase
7import sys
8import threading
9import socket
10import errno
11import time
12
13class Debugger(threading.Thread):
14    def __init__(self, port):
15        super().__init__()
16        self.events = []
17        self.success = False
18        self.port = port
19
20    def run(self):
21        # create listening socket
22        s = None
23        tstart = time.time()
24        while time.time() - tstart < 10:
25            try:
26                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
27                s.connect(('127.0.0.1', self.port))
28                break
29            except socket.error as exc:
30                if s:
31                    s.close()
32                if exc.args[0] != errno.ECONNREFUSED:
33                    raise
34                time.sleep(0.1)
35
36        if s is None:
37            self.events.append(['nosocket'])
38            return
39
40        s.setblocking(1)
41        s.settimeout(10.0)
42
43        output = s.makefile('w', 1)
44        input = s.makefile('r', 1)
45
46        try:
47            # handshake
48            l = input.readline().strip()
49            assert l == 'ANTLR 2'
50            l = input.readline().strip()
51            assert l.startswith('grammar "'), l
52
53            output.write('ACK\n')
54            output.flush()
55
56            while True:
57                event = input.readline().strip()
58                self.events.append(event.split('\t'))
59
60                output.write('ACK\n')
61                output.flush()
62
63                if event == 'terminate':
64                    self.success = True
65                    break
66
67        except socket.timeout:
68            self.events.append(['timeout'])
69        except socket.error as exc:
70            self.events.append(['socketerror', exc.args])
71        finally:
72            output.close()
73            input.close()
74            s.close()
75
76
77class T(testbase.ANTLRTest):
78    def execParser(self, grammar, grammarEntry, input, listener,
79                   parser_args={}):
80        if listener is None:
81            port = 49100
82            debugger = Debugger(port)
83            debugger.start()
84            # TODO(pink): install alarm, so it doesn't hang forever in case of a bug
85
86        else:
87            port = None
88
89        try:
90            lexerCls, parserCls = self.compileInlineGrammar(
91                grammar, options='-debug')
92
93            cStream = antlr3.StringStream(input)
94            lexer = lexerCls(cStream)
95            tStream = antlr3.CommonTokenStream(lexer)
96            parser = parserCls(tStream, dbg=listener, port=port, **parser_args)
97            getattr(parser, grammarEntry)()
98
99        finally:
100            if listener is None:
101                debugger.join()
102                return debugger
103
104    def testBasicParser(self):
105        grammar = textwrap.dedent(
106        r'''
107        grammar T;
108        options {
109            language=Python3;
110        }
111        a : ID EOF;
112        ID : 'a'..'z'+ ;
113        WS : (' '|'\n') {$channel=HIDDEN} ;
114        ''')
115
116        listener = antlr3.debug.RecordDebugEventListener()
117
118        self.execParser(
119            grammar, 'a',
120            input="a",
121            listener=listener)
122
123        # We only check that some LT events are present. How many is subject
124        # to change (at the time of writing there are two, which is one too
125        # many).
126        lt_events = [event for event in listener.events
127                     if event.startswith("LT ")]
128        self.assertNotEqual(lt_events, [])
129
130        # For the rest, filter out LT events to get a reliable test.
131        expected = ["enterRule a",
132                    "location 6:1",
133                    "location 6:5",
134                    "location 6:8",
135                    "location 6:11",
136                    "exitRule a"]
137        found = [event for event in listener.events
138                 if not event.startswith("LT ")]
139        self.assertListEqual(found, expected)
140
141    def testSocketProxy(self):
142        grammar = textwrap.dedent(
143        r'''
144        grammar T;
145        options {
146            language=Python3;
147        }
148        a : ID EOF;
149        ID : 'a'..'z'+ ;
150        WS : (' '|'\n') {$channel=HIDDEN} ;
151        ''')
152
153        debugger = self.execParser(
154            grammar, 'a',
155            input="a",
156            listener=None)
157
158        self.assertTrue(debugger.success)
159        expected = [['enterRule', 'T.g', 'a'],
160                    ['location', '6', '1'],
161                    ['enterAlt', '1'],
162                    ['location', '6', '5'],
163                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
164                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
165                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
166                    ['location', '6', '8'],
167                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
168                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
169                    ['consumeToken', '-1', '-1', '0', '1', '1', '"<EOF>'],
170                    ['location', '6', '11'],
171                    ['exitRule', 'T.g', 'a'],
172                    ['terminate']]
173
174        self.assertListEqual(debugger.events, expected)
175
176    def testRecognitionException(self):
177        grammar = textwrap.dedent(
178        r'''
179        grammar T;
180        options {
181            language=Python3;
182        }
183        a : ID EOF;
184        ID : 'a'..'z'+ ;
185        WS : (' '|'\n') {$channel=HIDDEN} ;
186        ''')
187
188        debugger = self.execParser(
189            grammar, 'a',
190            input="a b",
191            listener=None)
192
193        self.assertTrue(debugger.success)
194        expected = [['enterRule', 'T.g', 'a'],
195                    ['location', '6', '1'],
196                    ['enterAlt', '1'],
197                    ['location', '6', '5'],
198                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
199                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
200                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
201                    ['consumeHiddenToken', '1', '5', '99', '1', '1', '"'],
202                    ['location', '6', '8'],
203                    ['LT', '1', '2', '4', '0', '1', '2', '"b'],
204                    ['LT', '1', '2', '4', '0', '1', '2', '"b'],
205                    ['LT', '2', '-1', '-1', '0', '1', '3', '"<EOF>'],
206                    ['LT', '1', '2', '4', '0', '1', '2', '"b'],
207                    ['LT', '1', '2', '4', '0', '1', '2', '"b'],
208                    ['beginResync'],
209                    ['consumeToken', '2', '4', '0', '1', '2', '"b'],
210                    ['endResync'],
211                    ['exception', 'UnwantedTokenException', '2', '1', '2'],
212                    ['LT', '1', '-1', '-1', '0', '1', '3', '"<EOF>'],
213                    ['consumeToken', '-1', '-1', '0', '1', '3', '"<EOF>'],
214                    ['location', '6', '11'],
215                    ['exitRule', 'T.g', 'a'],
216                    ['terminate']]
217
218        self.assertListEqual(debugger.events, expected)
219
220
221    def testSemPred(self):
222        grammar = textwrap.dedent(
223        r'''
224        grammar T;
225        options {
226            language=Python3;
227        }
228        a : {True}? ID EOF;
229        ID : 'a'..'z'+ ;
230        WS : (' '|'\n') {$channel=HIDDEN} ;
231        ''')
232
233        debugger = self.execParser(
234            grammar, 'a',
235            input="a",
236            listener=None)
237
238        self.assertTrue(debugger.success)
239        expected = [['enterRule', 'T.g', 'a'],
240                    ['location', '6', '1'],
241                    ['enterAlt', '1'],
242                    ['location', '6', '5'],
243                    ['semanticPredicate', '1', 'True'],
244                    ['location', '6', '13'],
245                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
246                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
247                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
248                    ['location', '6', '16'],
249                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
250                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
251                    ['consumeToken', '-1', '-1', '0', '1', '1', '"<EOF>'],
252                    ['location', '6', '19'],
253                    ['exitRule', 'T.g', 'a'],
254                    ['terminate']]
255
256        self.assertListEqual(debugger.events, expected)
257
258
259    def testPositiveClosureBlock(self):
260        grammar = textwrap.dedent(
261        r'''
262        grammar T;
263        options {
264            language=Python3;
265        }
266        a : ID ( ID | INT )+ EOF;
267        ID : 'a'..'z'+ ;
268        INT : '0'..'9'+ ;
269        WS : (' '|'\n') {$channel=HIDDEN} ;
270        ''')
271
272        debugger = self.execParser(
273            grammar, 'a',
274            input="a 1 b c 3",
275            listener=None)
276
277        self.assertTrue(debugger.success)
278        expected = [['enterRule', 'T.g', 'a'],
279                    ['location', '6', '1'],
280                    ['enterAlt', '1'],
281                    ['location', '6', '5'],
282                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
283                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
284                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
285                    ['consumeHiddenToken', '1', '6', '99', '1', '1', '"'],
286                    ['location', '6', '8'],
287                    ['enterSubRule', '1'],
288                    ['enterDecision', '1', '0'],
289                    ['LT', '1', '2', '5', '0', '1', '2', '"1'],
290                    ['exitDecision', '1'],
291                    ['enterAlt', '1'],
292                    ['location', '6', '8'],
293                    ['LT', '1', '2', '5', '0', '1', '2', '"1'],
294                    ['consumeToken', '2', '5', '0', '1', '2', '"1'],
295                    ['consumeHiddenToken', '3', '6', '99', '1', '3', '"'],
296                    ['enterDecision', '1', '0'],
297                    ['LT', '1', '4', '4', '0', '1', '4', '"b'],
298                    ['exitDecision', '1'],
299                    ['enterAlt', '1'],
300                    ['location', '6', '8'],
301                    ['LT', '1', '4', '4', '0', '1', '4', '"b'],
302                    ['consumeToken', '4', '4', '0', '1', '4', '"b'],
303                    ['consumeHiddenToken', '5', '6', '99', '1', '5', '"'],
304                    ['enterDecision', '1', '0'],
305                    ['LT', '1', '6', '4', '0', '1', '6', '"c'],
306                    ['exitDecision', '1'],
307                    ['enterAlt', '1'],
308                    ['location', '6', '8'],
309                    ['LT', '1', '6', '4', '0', '1', '6', '"c'],
310                    ['consumeToken', '6', '4', '0', '1', '6', '"c'],
311                    ['consumeHiddenToken', '7', '6', '99', '1', '7', '"'],
312                    ['enterDecision', '1', '0'],
313                    ['LT', '1', '8', '5', '0', '1', '8', '"3'],
314                    ['exitDecision', '1'],
315                    ['enterAlt', '1'],
316                    ['location', '6', '8'],
317                    ['LT', '1', '8', '5', '0', '1', '8', '"3'],
318                    ['consumeToken', '8', '5', '0', '1', '8', '"3'],
319                    ['enterDecision', '1', '0'],
320                    ['LT', '1', '-1', '-1', '0', '1', '9', '"<EOF>'],
321                    ['exitDecision', '1'],
322                    ['exitSubRule', '1'],
323                    ['location', '6', '22'],
324                    ['LT', '1', '-1', '-1', '0', '1', '9', '"<EOF>'],
325                    ['LT', '1', '-1', '-1', '0', '1', '9', '"<EOF>'],
326                    ['consumeToken', '-1', '-1', '0', '1', '9', '"<EOF>'],
327                    ['location', '6', '25'],
328                    ['exitRule', 'T.g', 'a'],
329                    ['terminate']]
330
331        self.assertListEqual(debugger.events, expected)
332
333
334    def testClosureBlock(self):
335        grammar = textwrap.dedent(
336        r'''
337        grammar T;
338        options {
339            language=Python3;
340        }
341        a : ID ( ID | INT )* EOF;
342        ID : 'a'..'z'+ ;
343        INT : '0'..'9'+ ;
344        WS : (' '|'\n') {$channel=HIDDEN} ;
345        ''')
346
347        debugger = self.execParser(
348            grammar, 'a',
349            input="a 1 b c 3",
350            listener=None)
351
352        self.assertTrue(debugger.success)
353        expected = [['enterRule', 'T.g', 'a'],
354                    ['location', '6', '1'],
355                    ['enterAlt', '1'],
356                    ['location', '6', '5'],
357                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
358                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
359                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
360                    ['consumeHiddenToken', '1', '6', '99', '1', '1', '"'],
361                    ['location', '6', '8'],
362                    ['enterSubRule', '1'],
363                    ['enterDecision', '1', '0'],
364                    ['LT', '1', '2', '5', '0', '1', '2', '"1'],
365                    ['exitDecision', '1'],
366                    ['enterAlt', '1'],
367                    ['location', '6', '8'],
368                    ['LT', '1', '2', '5', '0', '1', '2', '"1'],
369                    ['consumeToken', '2', '5', '0', '1', '2', '"1'],
370                    ['consumeHiddenToken', '3', '6', '99', '1', '3', '"'],
371                    ['enterDecision', '1', '0'],
372                    ['LT', '1', '4', '4', '0', '1', '4', '"b'],
373                    ['exitDecision', '1'],
374                    ['enterAlt', '1'],
375                    ['location', '6', '8'],
376                    ['LT', '1', '4', '4', '0', '1', '4', '"b'],
377                    ['consumeToken', '4', '4', '0', '1', '4', '"b'],
378                    ['consumeHiddenToken', '5', '6', '99', '1', '5', '"'],
379                    ['enterDecision', '1', '0'],
380                    ['LT', '1', '6', '4', '0', '1', '6', '"c'],
381                    ['exitDecision', '1'],
382                    ['enterAlt', '1'],
383                    ['location', '6', '8'],
384                    ['LT', '1', '6', '4', '0', '1', '6', '"c'],
385                    ['consumeToken', '6', '4', '0', '1', '6', '"c'],
386                    ['consumeHiddenToken', '7', '6', '99', '1', '7', '"'],
387                    ['enterDecision', '1', '0'],
388                    ['LT', '1', '8', '5', '0', '1', '8', '"3'],
389                    ['exitDecision', '1'],
390                    ['enterAlt', '1'],
391                    ['location', '6', '8'],
392                    ['LT', '1', '8', '5', '0', '1', '8', '"3'],
393                    ['consumeToken', '8', '5', '0', '1', '8', '"3'],
394                    ['enterDecision', '1', '0'],
395                    ['LT', '1', '-1', '-1', '0', '1', '9', '"<EOF>'],
396                    ['exitDecision', '1'],
397                    ['exitSubRule', '1'],
398                    ['location', '6', '22'],
399                    ['LT', '1', '-1', '-1', '0', '1', '9', '"<EOF>'],
400                    ['LT', '1', '-1', '-1', '0', '1', '9', '"<EOF>'],
401                    ['consumeToken', '-1', '-1', '0', '1', '9', '"<EOF>'],
402                    ['location', '6', '25'],
403                    ['exitRule', 'T.g', 'a'],
404                    ['terminate']]
405
406        self.assertListEqual(debugger.events, expected)
407
408
409    def testMismatchedSetException(self):
410        grammar = textwrap.dedent(
411        r'''
412        grammar T;
413        options {
414            language=Python3;
415        }
416        a : ID ( ID | INT ) EOF;
417        ID : 'a'..'z'+ ;
418        INT : '0'..'9'+ ;
419        WS : (' '|'\n') {$channel=HIDDEN} ;
420        ''')
421
422        debugger = self.execParser(
423            grammar, 'a',
424            input="a",
425            listener=None)
426
427        self.assertTrue(debugger.success)
428        expected = [['enterRule', 'T.g', 'a'],
429                    ['location', '6', '1'],
430                    ['enterAlt', '1'],
431                    ['location', '6', '5'],
432                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
433                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
434                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
435                    ['location', '6', '8'],
436                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
437                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
438                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
439                    ['exception', 'MismatchedSetException', '1', '1', '1'],
440                    ['exception', 'MismatchedSetException', '1', '1', '1'],
441                    ['beginResync'],
442                    ['LT', '1', '-1', '-1', '0', '1', '1', '"<EOF>'],
443                    ['endResync'],
444                    ['location', '6', '24'],
445                    ['exitRule', 'T.g', 'a'],
446                    ['terminate']]
447
448        self.assertListEqual(debugger.events, expected)
449
450
451    def testBlock(self):
452        grammar = textwrap.dedent(
453        r'''
454        grammar T;
455        options {
456            language=Python3;
457        }
458        a : ID ( b | c ) EOF;
459        b : ID;
460        c : INT;
461        ID : 'a'..'z'+ ;
462        INT : '0'..'9'+ ;
463        WS : (' '|'\n') {$channel=HIDDEN} ;
464        ''')
465
466        debugger = self.execParser(
467            grammar, 'a',
468            input="a 1",
469            listener=None)
470
471        self.assertTrue(debugger.success)
472        expected =  [['enterRule', 'T.g', 'a'],
473                     ['location', '6', '1'],
474                     ['enterAlt', '1'],
475                     ['location', '6', '5'],
476                     ['LT', '1', '0', '4', '0', '1', '0', '"a'],
477                     ['LT', '1', '0', '4', '0', '1', '0', '"a'],
478                     ['consumeToken', '0', '4', '0', '1', '0', '"a'],
479                     ['consumeHiddenToken', '1', '6', '99', '1', '1', '"'],
480                     ['location', '6', '8'],
481                     ['enterSubRule', '1'],
482                     ['enterDecision', '1', '0'],
483                     ['LT', '1', '2', '5', '0', '1', '2', '"1'],
484                     ['exitDecision', '1'],
485                     ['enterAlt', '2'],
486                     ['location', '6', '14'],
487                     ['enterRule', 'T.g', 'c'],
488                     ['location', '8', '1'],
489                     ['enterAlt', '1'],
490                     ['location', '8', '5'],
491                     ['LT', '1', '2', '5', '0', '1', '2', '"1'],
492                     ['LT', '1', '2', '5', '0', '1', '2', '"1'],
493                     ['consumeToken', '2', '5', '0', '1', '2', '"1'],
494                     ['location', '8', '8'],
495                     ['exitRule', 'T.g', 'c'],
496                     ['exitSubRule', '1'],
497                     ['location', '6', '18'],
498                     ['LT', '1', '-1', '-1', '0', '1', '3', '"<EOF>'],
499                     ['LT', '1', '-1', '-1', '0', '1', '3', '"<EOF>'],
500                     ['consumeToken', '-1', '-1', '0', '1', '3', '"<EOF>'],
501                     ['location', '6', '21'],
502                     ['exitRule', 'T.g', 'a'],
503                     ['terminate']]
504
505        self.assertListEqual(debugger.events, expected)
506
507
508    def testNoViableAlt(self):
509        grammar = textwrap.dedent(
510        r'''
511        grammar T;
512        options {
513            language=Python3;
514        }
515        a : ID ( b | c ) EOF;
516        b : ID;
517        c : INT;
518        ID : 'a'..'z'+ ;
519        INT : '0'..'9'+ ;
520        BANG : '!' ;
521        WS : (' '|'\n') {$channel=HIDDEN} ;
522        ''')
523
524        debugger = self.execParser(
525            grammar, 'a',
526            input="a !",
527            listener=None)
528
529        self.assertTrue(debugger.success)
530        expected =  [['enterRule', 'T.g', 'a'],
531                     ['location', '6', '1'],
532                     ['enterAlt', '1'],
533                     ['location', '6', '5'],
534                     ['LT', '1', '0', '5', '0', '1', '0', '"a'],
535                     ['LT', '1', '0', '5', '0', '1', '0', '"a'],
536                     ['consumeToken', '0', '5', '0', '1', '0', '"a'],
537                     ['consumeHiddenToken', '1', '7', '99', '1', '1', '"'],
538                     ['location', '6', '8'],
539                     ['enterSubRule', '1'],
540                     ['enterDecision', '1', '0'],
541                     ['LT', '1', '2', '4', '0', '1', '2', '"!'],
542                     ['LT', '1', '2', '4', '0', '1', '2', '"!'],
543                     ['LT', '1', '2', '4', '0', '1', '2', '"!'],
544                     ['exception', 'NoViableAltException', '2', '1', '2'],
545                     ['exitDecision', '1'],
546                     ['exitSubRule', '1'],
547                     ['exception', 'NoViableAltException', '2', '1', '2'],
548                     ['beginResync'],
549                     ['LT', '1', '2', '4', '0', '1', '2', '"!'],
550                     ['consumeToken', '2', '4', '0', '1', '2', '"!'],
551                     ['LT', '1', '-1', '-1', '0', '1', '3', '"<EOF>'],
552                     ['endResync'],
553                     ['location', '6', '21'],
554                     ['exitRule', 'T.g', 'a'],
555                     ['terminate']]
556
557        self.assertListEqual(debugger.events, expected)
558
559
560    def testRuleBlock(self):
561        grammar = textwrap.dedent(
562        r'''
563        grammar T;
564        options {
565            language=Python3;
566        }
567        a : b | c;
568        b : ID;
569        c : INT;
570        ID : 'a'..'z'+ ;
571        INT : '0'..'9'+ ;
572        WS : (' '|'\n') {$channel=HIDDEN} ;
573        ''')
574
575        debugger = self.execParser(
576            grammar, 'a',
577            input="1",
578            listener=None)
579
580        self.assertTrue(debugger.success)
581        expected = [['enterRule', 'T.g', 'a'],
582                    ['location', '6', '1'],
583                    ['enterDecision', '1', '0'],
584                    ['LT', '1', '0', '5', '0', '1', '0', '"1'],
585                    ['exitDecision', '1'],
586                    ['enterAlt', '2'],
587                    ['location', '6', '9'],
588                    ['enterRule', 'T.g', 'c'],
589                    ['location', '8', '1'],
590                    ['enterAlt', '1'],
591                    ['location', '8', '5'],
592                    ['LT', '1', '0', '5', '0', '1', '0', '"1'],
593                    ['LT', '1', '0', '5', '0', '1', '0', '"1'],
594                    ['consumeToken', '0', '5', '0', '1', '0', '"1'],
595                    ['location', '8', '8'],
596                    ['exitRule', 'T.g', 'c'],
597                    ['location', '6', '10'],
598                    ['exitRule', 'T.g', 'a'],
599                    ['terminate']]
600
601        self.assertListEqual(debugger.events, expected)
602
603
604    def testRuleBlockSingleAlt(self):
605        grammar = textwrap.dedent(
606        r'''
607        grammar T;
608        options {
609            language=Python3;
610        }
611        a : b;
612        b : ID;
613        ID : 'a'..'z'+ ;
614        INT : '0'..'9'+ ;
615        WS : (' '|'\n') {$channel=HIDDEN} ;
616        ''')
617
618        debugger = self.execParser(
619            grammar, 'a',
620            input="a",
621            listener=None)
622
623        self.assertTrue(debugger.success)
624        expected = [['enterRule', 'T.g', 'a'],
625                    ['location', '6', '1'],
626                    ['enterAlt', '1'],
627                    ['location', '6', '5'],
628                    ['enterRule', 'T.g', 'b'],
629                    ['location', '7', '1'],
630                    ['enterAlt', '1'],
631                    ['location', '7', '5'],
632                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
633                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
634                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
635                    ['location', '7', '7'],
636                    ['exitRule', 'T.g', 'b'],
637                    ['location', '6', '6'],
638                    ['exitRule', 'T.g', 'a'],
639                    ['terminate']]
640
641        self.assertListEqual(debugger.events, expected)
642
643
644    def testBlockSingleAlt(self):
645        grammar = textwrap.dedent(
646        r'''
647        grammar T;
648        options {
649            language=Python3;
650        }
651        a : ( b );
652        b : ID;
653        ID : 'a'..'z'+ ;
654        INT : '0'..'9'+ ;
655        WS : (' '|'\n') {$channel=HIDDEN} ;
656        ''')
657
658        debugger = self.execParser(
659            grammar, 'a',
660            input="a",
661            listener=None)
662
663        self.assertTrue(debugger.success)
664        expected = [['enterRule', 'T.g', 'a'],
665                    ['location', '6', '1'],
666                    ['enterAlt', '1'],
667                    ['location', '6', '5'],
668                    ['enterAlt', '1'],
669                    ['location', '6', '7'],
670                    ['enterRule', 'T.g', 'b'],
671                    ['location', '7', '1'],
672                    ['enterAlt', '1'],
673                    ['location', '7', '5'],
674                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
675                    ['LT', '1', '0', '4', '0', '1', '0', '"a'],
676                    ['consumeToken', '0', '4', '0', '1', '0', '"a'],
677                    ['location', '7', '7'],
678                    ['exitRule', 'T.g', 'b'],
679                    ['location', '6', '10'],
680                    ['exitRule', 'T.g', 'a'],
681                    ['terminate']]
682
683        self.assertListEqual(debugger.events, expected)
684
685
686    def testDFA(self):
687        grammar = textwrap.dedent(
688        r'''
689        grammar T;
690        options {
691            language=Python3;
692        }
693        a : ( b | c ) EOF;
694        b : ID* INT;
695        c : ID+ BANG;
696        ID : 'a'..'z'+ ;
697        INT : '0'..'9'+ ;
698        BANG : '!';
699        WS : (' '|'\n') {$channel=HIDDEN} ;
700        ''')
701
702        debugger = self.execParser(
703            grammar, 'a',
704            input="a!",
705            listener=None)
706
707        self.assertTrue(debugger.success)
708        expected = [['enterRule', 'T.g', 'a'],
709                    ['location', '6', '1'],
710                    ['enterAlt', '1'],
711                    ['location', '6', '5'],
712                    ['enterSubRule', '1'],
713                    ['enterDecision', '1', '0'],
714                    ['mark', '0'],
715                    ['LT', '1', '0', '5', '0', '1', '0', '"a'],
716                    ['consumeToken', '0', '5', '0', '1', '0', '"a'],
717                    ['LT', '1', '1', '4', '0', '1', '1', '"!'],
718                    ['consumeToken', '1', '4', '0', '1', '1', '"!'],
719                    ['rewind', '0'],
720                    ['exitDecision', '1'],
721                    ['enterAlt', '2'],
722                    ['location', '6', '11'],
723                    ['enterRule', 'T.g', 'c'],
724                    ['location', '8', '1'],
725                    ['enterAlt', '1'],
726                    ['location', '8', '5'],
727                    ['enterSubRule', '3'],
728                    ['enterDecision', '3', '0'],
729                    ['LT', '1', '0', '5', '0', '1', '0', '"a'],
730                    ['exitDecision', '3'],
731                    ['enterAlt', '1'],
732                    ['location', '8', '5'],
733                    ['LT', '1', '0', '5', '0', '1', '0', '"a'],
734                    ['LT', '1', '0', '5', '0', '1', '0', '"a'],
735                    ['consumeToken', '0', '5', '0', '1', '0', '"a'],
736                    ['enterDecision', '3', '0'],
737                    ['LT', '1', '1', '4', '0', '1', '1', '"!'],
738                    ['exitDecision', '3'],
739                    ['exitSubRule', '3'],
740                    ['location', '8', '9'],
741                    ['LT', '1', '1', '4', '0', '1', '1', '"!'],
742                    ['LT', '1', '1', '4', '0', '1', '1', '"!'],
743                    ['consumeToken', '1', '4', '0', '1', '1', '"!'],
744                    ['location', '8', '13'],
745                    ['exitRule', 'T.g', 'c'],
746                    ['exitSubRule', '1'],
747                    ['location', '6', '15'],
748                    ['LT', '1', '-1', '-1', '0', '1', '2', '"<EOF>'],
749                    ['LT', '1', '-1', '-1', '0', '1', '2', '"<EOF>'],
750                    ['consumeToken', '-1', '-1', '0', '1', '2', '"<EOF>'],
751                    ['location', '6', '18'],
752                    ['exitRule', 'T.g', 'a'],
753                    ['terminate']]
754
755        self.assertListEqual(debugger.events, expected)
756
757
758    def testBasicAST(self):
759        grammar = textwrap.dedent(
760        r'''
761        grammar T;
762        options {
763            language=Python3;
764            output=AST;
765        }
766        a : ( b | c ) EOF!;
767        b : ID* INT -> ^(INT ID*);
768        c : ID+ BANG -> ^(BANG ID+);
769        ID : 'a'..'z'+ ;
770        INT : '0'..'9'+ ;
771        BANG : '!';
772        WS : (' '|'\n') {$channel=HIDDEN} ;
773        ''')
774
775        listener = antlr3.debug.RecordDebugEventListener()
776
777        self.execParser(
778            grammar, 'a',
779            input="a!",
780            listener=listener)
781
782        # don't check output for now (too dynamic), I'm satisfied if it
783        # doesn't crash
784
785
786if __name__ == '__main__':
787    unittest.main()
788