debug-mode.rb revision 324c4644fee44b9898524c09511bd33c3f12e2df
1#!/usr/bin/ruby
2# encoding: utf-8
3
4require 'antlr3'
5require 'fileutils'
6require 'antlr3/test/functional'
7#require 'antlr3/test/diff'
8
9class ANTLRDebugger < Thread
10  self.abort_on_exception = true
11  attr_accessor :events, :success, :port
12  include Timeout
13  
14  def initialize( port )
15    @events = []
16    @success = false
17    @port = port
18    
19    super do
20      timeout( 2 ) do
21        begin
22          @socket = TCPSocket.open( 'localhost', @port )
23          #Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
24          #@socket.connect( Socket.pack_sockaddr_in(@port, '127.0.0.1') )
25        rescue Errno::ECONNREFUSED => error
26          if $VERBOSE
27            $stderr.printf( 
28                "%s:%s received connection refuse error: %p\n",
29                __FILE__, __LINE__, error
30              )
31            $stderr.puts( "sleeping for 0.1 seconds before retrying" )
32          end
33          sleep( 0.01 )
34          retry
35        end
36      end
37      
38      @socket.readline.strip.should == 'ANTLR 2'
39      @socket.readline.strip.start_with?( 'grammar "' ).should == true
40      ack
41      loop do
42        event = @socket.readline.strip
43        @events << event.split( "\t" )
44        ack
45        break if event == 'terminate'
46      end
47      
48      @socket.close
49      @success = true
50    end
51    
52  end
53  
54  def ack
55    @socket.write( "ACK\n" )
56    @socket.flush
57  end
58
59end # ANTLRDebugger
60
61class TestDebugGrammars < ANTLR3::Test::Functional
62  compile_options :debug => true
63  
64  #include ANTLR3::Test::Diff
65  
66  def parse( grammar, rule, input, options = {} )
67    @grammar = inline_grammar( grammar )
68    @grammar.compile( self.class.compile_options )
69    @grammar_path = File.expand_path( @grammar.path )
70    for output_file in @grammar.target_files
71      self.class.import( output_file )
72    end
73    grammar_module = self.class.const_get( @grammar.name )
74    listener = options[ :listener ] or debugger = ANTLRDebugger.new( port = 49100 )
75    
76    begin
77      lexer = grammar_module::Lexer.new( input )
78      tokens = ANTLR3::CommonTokenStream.new( lexer )
79      options[ :debug_listener ] = listener
80      parser = grammar_module::Parser.new( tokens, options )
81      parser.send( rule )
82    ensure
83      if listener.nil?
84        debugger.join
85        return( debugger )
86      end
87    end
88  end
89  
90  example 'basic debug-mode parser using a RecordEventListener' do
91    grammar = %q<
92      grammar BasicParser;                      // line 1
93      options {language=Ruby;}                  // line 2
94      a : ID EOF;                               // line 3
95      ID : 'a'..'z'+ ;                          // line 4
96      WS : (' '|'\n') {$channel=HIDDEN;} ;
97    >
98    listener = ANTLR3::Debug::RecordEventListener.new
99    parse( grammar, :a, 'a', :listener => listener )
100    lt_events, found = listener.events.partition { |event| event.start_with?( "(look): " ) }
101    lt_events.should_not be_empty
102    
103    expected = [ "(enter_rule): rule=a",
104                "(location): line=3 position=1",
105                "(enter_alternative): number=1",
106                "(location): line=3 position=5",
107                "(location): line=3 position=8",
108                "(location): line=3 position=11",
109                "(exit_rule): rule=a" ]
110    found.should == expected
111  end
112  
113  example 'debug-mode parser using a socket proxy to transmit events' do
114    grammar = %q<
115      grammar SocketProxy;                   // line 1
116      options {language=Ruby;}               // line 2
117      a : ID EOF;                           // line 3
118      ID : 'a'..'z'+ ;                       // line 4
119      WS : (' '|'\n') {$channel=HIDDEN;} ;
120    >
121    debugger = parse( grammar, :a, 'a' )
122    debugger.success.should be_true
123    expected = [ 
124      [ 'enter_rule', @grammar_path, 'a' ],
125      [ 'location', '3', '1' ],
126      [ 'enter_alternative', '1' ],
127      [ 'location', '3', '5' ],
128      [ 'look', '1', '0', '4', 'default', '1', '0', '"a"' ],
129      [ 'look', '1', '0', '4', 'default', '1', '0', '"a"' ],
130      [ 'consume_token', '0', '4', 'default', '1', '0', '"a"' ],
131      [ 'location', '3', '8' ],
132      [ 'look', '1', '-1', '-1', 'default', '0', '-1', 'nil' ],
133      [ 'look', '1', '-1', '-1', 'default', '0', '-1', 'nil' ],
134      [ 'consume_token', '-1', '-1', 'default', '0', '-1', 'nil' ],
135      [ 'location', '3', '11' ],
136      [ 'exit_rule', @grammar_path, 'a' ],
137      [ 'terminate' ]
138    ]
139    
140    debugger.events.should == expected
141  end
142  
143  example 'debug-mode parser events triggered by recognition errors' do
144    grammar = %q<
145      grammar RecognitionError;
146      options { language=Ruby; }
147      a : ID EOF;
148      ID : 'a'..'z'+ ;
149      WS : (' '|'\n') {$channel=HIDDEN;} ;
150    >
151    debugger = parse( grammar, :a, "a b" )
152    debugger.success.should be_true
153    
154    expected = [ 
155      [ "enter_rule", @grammar_path, "a" ],
156      [ "location", "3", "1" ],
157      [ "enter_alternative", "1" ],
158      [ "location", "3", "5" ],
159      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
160      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
161      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
162      [ "consume_hidden_token", "1", "5", "hidden", "1", "1", '" "' ],
163      [ "location", "3", "8" ],
164      [ "look", "1", "2", "4", "default", "1", "2", "\"b\"" ],
165      [ "look", "1", "2", "4", "default", "1", "2", "\"b\"" ],
166      [ "look", "2", "-1", "-1", "default", "0", "-1", "nil" ],
167      [ "look", "1", "2", "4", "default", "1", "2", "\"b\"" ],
168      [ "begin_resync" ],
169      [ "consume_token", "2", "4", "default", "1", "2", "\"b\"" ],
170      [ "end_resync" ],
171      [ "recognition_exception", "ANTLR3::Error::UnwantedToken", "2", "1", "2" ],
172      [ "consume_token", "-1", "-1", "default", "0", "-1", "nil" ],
173      [ "location", "3", "11" ],
174      [ "exit_rule", @grammar_path, "a" ],
175      [ "terminate" ]
176    ]
177    debugger.events.should == expected
178  end
179  
180  example 'debug-mode parser events triggered by semantic predicate evaluation' do
181    grammar = %q<
182      grammar SemPred;
183      options { language=Ruby; }
184      a : {true}? ID EOF;
185      ID : 'a'..'z'+ ;
186      WS : (' '|'\n') {$channel=HIDDEN;} ;
187    >
188    
189    debugger = parse( grammar, :a, "a" )
190    debugger.success.should be_true
191    
192    expected = [ 
193      [ "enter_rule", @grammar_path, "a" ],
194      [ "location", "3", "1" ],
195      [ "enter_alternative", "1" ],
196      [ "location", "3", "5" ],
197      [ "semantic_predicate", "true", '"true"' ],
198      [ "location", "3", "13" ],
199      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
200      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
201      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
202      [ "location", "3", "16" ],
203      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
204      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
205      [ "consume_token", "-1", "-1", "default", "0", "-1", "nil" ],
206      [ "location", "3", "19" ],
207      [ "exit_rule", @grammar_path, "a" ],
208      [ "terminate" ]
209    ]
210    debugger.events.should == expected
211  end
212  
213  example 'debug-mode parser events triggered by recognizing a (...)+ block' do
214    grammar = %q<
215      grammar PositiveClosureBlock;
216      options { language=Ruby; }
217      a : ID ( ID | INT )+ EOF;
218      ID : 'a'..'z'+ ;
219      INT : '0'..'9'+ ;
220      WS : (' '|'\n') {$channel=HIDDEN;} ;
221    >
222    
223    debugger = parse( grammar, :a, "a 1 b c 3" )
224    debugger.success.should be_true
225    
226    expected = [ 
227      [ "enter_rule", @grammar_path, "a" ],
228      [ "location", "3", "1" ],
229      [ "enter_alternative", "1" ],
230      [ "location", "3", "5" ],
231      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
232      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
233      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
234      [ "consume_hidden_token", "1", "6", "hidden", "1", "1", '" "' ],
235      [ "location", "3", "8" ],
236      [ "enter_subrule", "1" ],
237      [ "enter_decision", "1" ],
238      [ "look", "1", "2", "5", "default", "1", "2", "\"1\"" ],
239      [ "exit_decision", "1" ],
240      [ "enter_alternative", "1" ],
241      [ "location", "3", "8" ],
242      [ "look", "1", "2", "5", "default", "1", "2", "\"1\"" ],
243      [ "consume_token", "2", "5", "default", "1", "2", "\"1\"" ],
244      [ "consume_hidden_token", "3", "6", "hidden", "1", "3", '" "' ],
245      [ "enter_decision", "1" ],
246      [ "look", "1", "4", "4", "default", "1", "4", "\"b\"" ],
247      [ "exit_decision", "1" ],
248      [ "enter_alternative", "1" ],
249      [ "location", "3", "8" ],
250      [ "look", "1", "4", "4", "default", "1", "4", "\"b\"" ],
251      [ "consume_token", "4", "4", "default", "1", "4", "\"b\"" ],
252      [ "consume_hidden_token", "5", "6", "hidden", "1", "5", '" "' ],
253      [ "enter_decision", "1" ],
254      [ "look", "1", "6", "4", "default", "1", "6", "\"c\"" ],
255      [ "exit_decision", "1" ],
256      [ "enter_alternative", "1" ],
257      [ "location", "3", "8" ],
258      [ "look", "1", "6", "4", "default", "1", "6", "\"c\"" ],
259      [ "consume_token", "6", "4", "default", "1", "6", "\"c\"" ],
260      [ "consume_hidden_token", "7", "6", "hidden", "1", "7", '" "' ],
261      [ "enter_decision", "1" ],
262      [ "look", "1", "8", "5", "default", "1", "8", "\"3\"" ],
263      [ "exit_decision", "1" ],
264      [ "enter_alternative", "1" ],
265      [ "location", "3", "8" ],
266      [ "look", "1", "8", "5", "default", "1", "8", "\"3\"" ],
267      [ "consume_token", "8", "5", "default", "1", "8", "\"3\"" ],
268      [ "enter_decision", "1" ],
269      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
270      [ "exit_decision", "1" ],
271      [ "exit_subrule", "1" ],
272      [ "location", "3", "22" ],
273      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
274      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
275      [ "consume_token", "-1", "-1", "default", "0", "-1", "nil" ],
276      [ "location", "3", "25" ],
277      [ "exit_rule", @grammar_path, "a" ],
278      [ "terminate" ]
279    ]
280    
281    debugger.events.should == expected
282  end
283  
284  example 'debug-mode parser events triggered by recognizing a (...)* block' do
285    grammar = %q<
286      grammar ClosureBlock;
287      options { language=Ruby; }
288      a : ID ( ID | INT )* EOF;
289      ID : 'a'..'z'+ ;
290      INT : '0'..'9'+ ;
291      WS : (' '|'\n') {$channel=HIDDEN;} ;
292    >
293    
294    debugger = parse( grammar, :a, "a 1 b c 3" )
295    debugger.success.should be_true
296    
297    expected = [ 
298      [ "enter_rule", @grammar_path, "a" ],
299      [ "location", "3", "1" ],
300      [ "enter_alternative", "1" ],
301      [ "location", "3", "5" ],
302      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
303      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
304      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
305      [ "consume_hidden_token", "1", "6", "hidden", "1", "1", '" "' ],
306      [ "location", "3", "8" ],
307      [ "enter_subrule", "1" ],
308      [ "enter_decision", "1" ],
309      [ "look", "1", "2", "5", "default", "1", "2", "\"1\"" ],
310      [ "exit_decision", "1" ],
311      [ "enter_alternative", "1" ],
312      [ "location", "3", "8" ],
313      [ "look", "1", "2", "5", "default", "1", "2", "\"1\"" ],
314      [ "consume_token", "2", "5", "default", "1", "2", "\"1\"" ],
315      [ "consume_hidden_token", "3", "6", "hidden", "1", "3", '" "' ],
316      [ "enter_decision", "1" ],
317      [ "look", "1", "4", "4", "default", "1", "4", "\"b\"" ],
318      [ "exit_decision", "1" ],
319      [ "enter_alternative", "1" ],
320      [ "location", "3", "8" ],
321      [ "look", "1", "4", "4", "default", "1", "4", "\"b\"" ],
322      [ "consume_token", "4", "4", "default", "1", "4", "\"b\"" ],
323      [ "consume_hidden_token", "5", "6", "hidden", "1", "5", '" "' ],
324      [ "enter_decision", "1" ],
325      [ "look", "1", "6", "4", "default", "1", "6", "\"c\"" ],
326      [ "exit_decision", "1" ],
327      [ "enter_alternative", "1" ],
328      [ "location", "3", "8" ],
329      [ "look", "1", "6", "4", "default", "1", "6", "\"c\"" ],
330      [ "consume_token", "6", "4", "default", "1", "6", "\"c\"" ],
331      [ "consume_hidden_token", "7", "6", "hidden", "1", "7", '" "' ],
332      [ "enter_decision", "1" ],
333      [ "look", "1", "8", "5", "default", "1", "8", "\"3\"" ],
334      [ "exit_decision", "1" ],
335      [ "enter_alternative", "1" ],
336      [ "location", "3", "8" ],
337      [ "look", "1", "8", "5", "default", "1", "8", "\"3\"" ],
338      [ "consume_token", "8", "5", "default", "1", "8", "\"3\"" ],
339      [ "enter_decision", "1" ],
340      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
341      [ "exit_decision", "1" ],
342      [ "exit_subrule", "1" ],
343      [ "location", "3", "22" ],
344      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
345      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
346      [ "consume_token", "-1", "-1", "default", "0", "-1", "nil" ],
347      [ "location", "3", "25" ],
348      [ "exit_rule", @grammar_path, "a" ],
349      [ "terminate" ]
350    ]
351    debugger.events.should == expected
352  end
353  
354  example 'debug-mode parser events triggered by a mismatched set error' do
355    grammar = %q<
356      grammar MismatchedSetError;
357      options { language=Ruby; }
358      a : ID ( ID | INT ) EOF;
359      ID : 'a'..'z'+ ;
360      INT : '0'..'9'+ ;
361      WS : (' '|'\n') {$channel=HIDDEN;} ;
362    >
363    
364    debugger = parse( grammar, :a, "a" )
365    debugger.success.should be_true
366    
367    expected = [ 
368      [ "enter_rule", @grammar_path, "a" ],
369      [ "location", "3", "1" ],
370      [ "enter_alternative", "1" ],
371      [ "location", "3", "5" ],
372      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
373      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
374      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
375      [ "location", "3", "8" ],
376      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
377      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
378      [ "recognition_exception", "ANTLR3::Error::MismatchedSet", "1", "0", "-1" ],
379      [ "recognition_exception", "ANTLR3::Error::MismatchedSet", "1", "0", "-1" ],
380      [ "begin_resync" ],
381      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
382      [ "end_resync" ],
383      [ "location", "3", "24" ],
384      [ "exit_rule", @grammar_path, "a" ],
385      [ "terminate" ]
386    ]
387    
388    debugger.events.should == expected
389  end
390  
391  example 'debug-mode parser block-location events for subrules' do
392    grammar = %q<
393      grammar Block;
394      options { language=Ruby; }
395      a : ID ( b | c ) EOF;
396      b : ID;
397      c : INT;
398      ID : 'a'..'z'+ ;
399      INT : '0'..'9'+ ;
400      WS : (' '|'\n') {$channel=HIDDEN;} ;
401    >
402    
403    debugger = parse( grammar, :a, "a 1" )
404    debugger.success.should be_true
405    
406    expected = [ 
407      [ "enter_rule", @grammar_path, "a" ],
408      [ "location", "3", "1" ],
409      [ "enter_alternative", "1" ],
410      [ "location", "3", "5" ],
411      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
412      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
413      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
414      [ "consume_hidden_token", "1", "6", "hidden", "1", "1", '" "' ],
415      [ "location", "3", "8" ],
416      [ "enter_subrule", "1" ],
417      [ "enter_decision", "1" ],
418      [ "look", "1", "2", "5", "default", "1", "2", "\"1\"" ],
419      [ "exit_decision", "1" ],
420      [ "enter_alternative", "2" ],
421      [ "location", "3", "14" ],
422      [ "enter_rule", @grammar_path, "c" ],
423      [ "location", "5", "1" ],
424      [ "enter_alternative", "1" ],
425      [ "location", "5", "5" ],
426      [ "look", "1", "2", "5", "default", "1", "2", "\"1\"" ],
427      [ "look", "1", "2", "5", "default", "1", "2", "\"1\"" ],
428      [ "consume_token", "2", "5", "default", "1", "2", "\"1\"" ],
429      [ "location", "5", "8" ],
430      [ "exit_rule", @grammar_path, "c" ],
431      [ "exit_subrule", "1" ],
432      [ "location", "3", "18" ],
433      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
434      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
435      [ "consume_token", "-1", "-1", "default", "0", "-1", "nil" ],
436      [ "location", "3", "21" ],
437      [ "exit_rule", @grammar_path, "a" ],
438      [ "terminate" ]
439    ]
440    debugger.events.should == expected
441  end
442  
443  example 'debug-mode parser events triggered by a no viable alternative error' do
444    grammar = %q<
445      grammar NoViableAlt;
446      options { language=Ruby; }
447      a : ID ( b | c ) EOF;
448      b : ID;
449      c : INT;
450      ID : 'a'..'z'+ ;
451      INT : '0'..'9'+ ;
452      BANG : '!' ;
453      WS : (' '|'\n') {$channel=HIDDEN;} ;
454    >
455    
456    debugger = parse( grammar, :a, "a !" )
457    debugger.success.should be_true
458    
459    expected = [ 
460      [ "enter_rule", @grammar_path, "a" ],
461      [ "location", "3", "1" ],
462      [ "enter_alternative", "1" ],
463      [ "location", "3", "5" ],
464      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
465      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
466      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
467      [ "consume_hidden_token", "1", "7", "hidden", "1", "1", '" "' ],
468      [ "location", "3", "8" ],
469      [ "enter_subrule", "1" ],
470      [ "enter_decision", "1" ],
471      [ "look", "1", "2", "6", "default", "1", "2", "\"!\"" ],
472      [ "look", "1", "2", "6", "default", "1", "2", "\"!\"" ],
473      [ "recognition_exception", "ANTLR3::Error::NoViableAlternative", "2", "1", "2" ],
474      [ "exit_decision", "1" ],
475      [ "exit_subrule", "1" ],
476      [ "recognition_exception", "ANTLR3::Error::NoViableAlternative", "2", "1", "2" ],
477      [ "begin_resync" ],
478      [ "look", "1", "2", "6", "default", "1", "2", "\"!\"" ],
479      [ "consume_token", "2", "6", "default", "1", "2", "\"!\"" ],
480      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
481      [ "end_resync" ],
482      [ "location", "3", "21" ],
483      [ "exit_rule", @grammar_path, "a" ],
484      [ "terminate" ]
485    ]
486    debugger.events.should == expected
487  end
488  
489  example 'debug-mode parser block-location events triggered by rules' do
490    grammar = %q<
491      grammar RuleBlock;
492      options { language=Ruby; }
493      a : b | c;
494      b : ID;
495      c : INT;
496      ID : 'a'..'z'+ ;
497      INT : '0'..'9'+ ;
498      WS : (' '|'\n') {$channel=HIDDEN;} ;
499    >
500    
501    debugger = parse( grammar, :a, "1" )
502    debugger.success.should be_true
503    
504    expected = [ 
505      [ "enter_rule", @grammar_path, "a" ],
506      [ "location", "3", "1" ],
507      [ "enter_decision", "1" ],
508      [ "look", "1", "0", "5", "default", "1", "0", "\"1\"" ],
509      [ "exit_decision", "1" ],
510      [ "enter_alternative", "2" ],
511      [ "location", "3", "9" ],
512      [ "enter_rule", @grammar_path, "c" ],
513      [ "location", "5", "1" ],
514      [ "enter_alternative", "1" ],
515      [ "location", "5", "5" ],
516      [ "look", "1", "0", "5", "default", "1", "0", "\"1\"" ],
517      [ "look", "1", "0", "5", "default", "1", "0", "\"1\"" ],
518      [ "consume_token", "0", "5", "default", "1", "0", "\"1\"" ],
519      [ "location", "5", "8" ],
520      [ "exit_rule", @grammar_path, "c" ],
521      [ "location", "3", "10" ],
522      [ "exit_rule", @grammar_path, "a" ],
523      [ "terminate" ]
524    ]
525    
526    debugger.events.should == expected
527  end
528  
529  example 'debug-mode parser block-location events triggered by single-alternative rules' do
530    grammar = %q<
531      grammar RuleBlockSingleAlt;
532      options { language=Ruby; }
533      a : b;
534      b : ID;
535      ID : 'a'..'z'+ ;
536      INT : '0'..'9'+ ;
537      WS : (' '|'\n') {$channel=HIDDEN;} ;
538    >
539    
540    debugger = parse( grammar, :a, "a" )
541    debugger.success.should be_true
542    
543    expected = [ 
544      [ "enter_rule", @grammar_path, "a" ],
545      [ "location", "3", "1" ],
546      [ "enter_alternative", "1" ],
547      [ "location", "3", "5" ],
548      [ "enter_rule", @grammar_path, "b" ],
549      [ "location", "4", "1" ],
550      [ "enter_alternative", "1" ],
551      [ "location", "4", "5" ],
552      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
553      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
554      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
555      [ "location", "4", "7" ],
556      [ "exit_rule", @grammar_path, "b" ],
557      [ "location", "3", "6" ],
558      [ "exit_rule", @grammar_path, "a" ],
559      [ "terminate" ]
560    ]
561    
562    debugger.events.should == expected
563  end
564  
565  example 'debug-mode parser block-location events triggered by single-alternative subrules' do
566    grammar = %q<
567      grammar BlockSingleAlt;
568      options { language=Ruby; }
569      a : ( b );
570      b : ID;
571      ID : 'a'..'z'+ ;
572      INT : '0'..'9'+ ;
573      WS : (' '|'\n') {$channel=HIDDEN;} ;
574    >
575    
576    debugger = parse( grammar, :a, "a" )
577    debugger.success.should be_true
578    
579    expected = [ 
580      [ "enter_rule", @grammar_path, "a" ],
581      [ "location", "3", "1" ],
582      [ "enter_alternative", "1" ],
583      [ "location", "3", "5" ],
584      [ "enter_alternative", "1" ],
585      [ "location", "3", "7" ],
586      [ "enter_rule", @grammar_path, "b" ],
587      [ "location", "4", "1" ],
588      [ "enter_alternative", "1" ],
589      [ "location", "4", "5" ],
590      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
591      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
592      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
593      [ "location", "4", "7" ],
594      [ "exit_rule", @grammar_path, "b" ],
595      [ "location", "3", "10" ],
596      [ "exit_rule", @grammar_path, "a" ],
597      [ "terminate" ]
598    ]
599    debugger.events.should == expected
600  end
601  
602  example 'debug-mode parser block-location events triggered by invoking a cyclic DFA for prediction' do
603    grammar = %q<
604      grammar DFA;
605      options { language=Ruby; }
606      a : ( b | c ) EOF;
607      b : ID* INT;
608      c : ID+ BANG;
609      ID : 'a'..'z'+ ;
610      INT : '0'..'9'+ ;
611      BANG : '!';
612      WS : (' '|'\n') {$channel=HIDDEN;} ;
613    >
614    
615    debugger = parse( grammar, :a, "a!" )
616    debugger.success.should be_true
617    
618    expected = [ 
619      [ "enter_rule", @grammar_path, "a" ],
620      [ "location", "3", "1" ],
621      [ "enter_alternative", "1" ],
622      [ "location", "3", "5" ],
623      [ "enter_subrule", "1" ],
624      [ "enter_decision", "1" ],
625      [ "mark", "0" ],
626      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
627      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
628      [ "look", "1", "1", "6", "default", "1", "1", "\"!\"" ],
629      [ "consume_token", "1", "6", "default", "1", "1", "\"!\"" ],
630      [ "rewind", "0" ],
631      [ "exit_decision", "1" ],
632      [ "enter_alternative", "2" ],
633      [ "location", "3", "11" ],
634      [ "enter_rule", @grammar_path, "c" ],
635      [ "location", "5", "1" ],
636      [ "enter_alternative", "1" ],
637      [ "location", "5", "5" ],
638      [ "enter_subrule", "3" ],
639      [ "enter_decision", "3" ],
640      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
641      [ "exit_decision", "3" ],
642      [ "enter_alternative", "1" ],
643      [ "location", "5", "5" ],
644      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
645      [ "look", "1", "0", "4", "default", "1", "0", "\"a\"" ],
646      [ "consume_token", "0", "4", "default", "1", "0", "\"a\"" ],
647      [ "enter_decision", "3" ],
648      [ "look", "1", "1", "6", "default", "1", "1", "\"!\"" ],
649      [ "exit_decision", "3" ],
650      [ "exit_subrule", "3" ],
651      [ "location", "5", "9" ],
652      [ "look", "1", "1", "6", "default", "1", "1", "\"!\"" ],
653      [ "look", "1", "1", "6", "default", "1", "1", "\"!\"" ],
654      [ "consume_token", "1", "6", "default", "1", "1", "\"!\"" ],
655      [ "location", "5", "13" ],
656      [ "exit_rule", @grammar_path, "c" ],
657      [ "exit_subrule", "1" ],
658      [ "location", "3", "15" ],
659      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
660      [ "look", "1", "-1", "-1", "default", "0", "-1", "nil" ],
661      [ "consume_token", "-1", "-1", "default", "0", "-1", "nil" ],
662      [ "location", "3", "18" ],
663      [ "exit_rule", @grammar_path, "a" ],
664      [ "terminate" ]
665    ]
666    debugger.events.should == expected
667  end
668  
669  example 'debug-mode AST-building parser events' do
670    grammar = %q/
671      grammar BasicAST;
672      options {
673        language=Ruby;
674        output=AST;
675      }
676      a : ( b | c ) EOF!;
677      b : ID* INT -> ^(INT ID*);
678      c : ID+ BANG -> ^(BANG ID+);
679      ID : 'a'..'z'+ ;
680      INT : '0'..'9'+ ;
681      BANG : '!';
682      WS : (' '|'\n') {$channel=HIDDEN;} ;
683    /
684    listener = ANTLR3::Debug::RecordEventListener.new
685    parse( grammar, :a, "a!", :listener => listener )
686  end
687
688end
689