1#!/usr/bin/ruby
2# encoding: utf-8
3
4require 'socket'
5
6module ANTLR3
7module Debug
8
9
10=begin rdoc ANTLR3::Debug::EventSocketProxy
11
12A proxy debug event listener that forwards events over a socket to
13a debugger (or any other listener) using a simple text-based protocol;
14one event per line.  ANTLRWorks listens on server socket with a
15RemoteDebugEventSocketListener instance.  These two objects must therefore
16be kept in sync.  New events must be handled on both sides of socket.
17
18=end
19class EventSocketProxy
20  include EventListener
21  
22  SOCKET_ADDR_PACK = 'snCCCCa8'.freeze
23  
24  def initialize( recognizer, options = {} )
25    super()
26    @grammar_file_name = recognizer.grammar_file_name
27    @adaptor = options[ :adaptor ]
28    @port = options[ :port ] || DEFAULT_PORT
29    @log = options[ :log ]
30    @socket = nil
31    @connection = nil
32  end
33  
34  def log!( message, *interpolation_arguments )
35    @log and @log.printf( message, *interpolation_arguments )
36  end
37  
38  def handshake
39    unless @socket
40      begin
41        @socket = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
42        @socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
43        @socket.bind( Socket.pack_sockaddr_in( @port, '' ) )
44        @socket.listen( 1 )
45        log!( "waiting for incoming connection on port %i\n", @port )
46        
47        @connection, addr = @socket.accept
48        port, host = Socket.unpack_sockaddr_in( addr )
49        log!( "Accepted connection from %s:%s\n", host, port )
50        
51        @connection.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
52        
53        write( 'ANTLR %s', PROTOCOL_VERSION )
54        write( 'grammar %p', @grammar_file_name )
55        ack
56      rescue IOError => error
57        log!( "handshake failed due to an IOError:\n" )
58        log!( "  %s: %s", error.class, error.message )
59        log!( "  Backtrace: " )
60        log!( "  - %s", error.backtrace.join( "\n  - " ) )
61        @connection and @connection.close
62        @socket and @socket.close
63        @socket = nil
64        raise
65      end
66    end
67    return self
68  end
69  
70  def write( message, *interpolation_arguments )
71    message << ?\n
72    log!( "---> #{ message }", *interpolation_arguments )
73    @connection.printf( message, *interpolation_arguments )
74    @connection.flush
75  end
76  
77  def ack
78    line = @connection.readline
79    log!( "<--- %s", line )
80    line
81  end
82  
83  def transmit( event, *interpolation_arguments )
84    write( event, *interpolation_arguments )
85    ack()
86  rescue IOError
87    @connection.close
88    raise
89  end
90  
91  def commence
92    # don't bother sending event; listener will trigger upon connection
93  end
94  
95  def terminate
96    transmit 'terminate'
97    @connection.close
98    @socket.close
99  end
100  
101  def enter_rule( grammar_file_name, rule_name )
102    transmit "%s\t%s\t%s", :enter_rule, grammar_file_name, rule_name
103  end
104  
105  def enter_alternative( alt )
106    transmit "%s\t%s", :enter_alternative, alt
107  end
108  
109  def exit_rule( grammar_file_name, rule_name )
110    transmit "%s\t%s\t%s", :exit_rule, grammar_file_name, rule_name
111  end
112  
113  def enter_subrule( decision_number )
114    transmit "%s\t%i", :enter_subrule, decision_number
115  end
116  
117  def exit_subrule( decision_number )
118    transmit "%s\t%i", :exit_subrule, decision_number
119  end
120  
121  def enter_decision( decision_number )
122    transmit "%s\t%i", :enter_decision, decision_number
123  end
124  
125  def exit_decision( decision_number )
126    transmit "%s\t%i", :exit_decision, decision_number
127  end
128  
129  def consume_token( token )
130    transmit "%s\t%s", :consume_token, serialize_token( token )
131  end
132  
133  def consume_hidden_token( token )
134    transmit "%s\t%s", :consume_hidden_token, serialize_token( token )
135  end
136  
137  def look( i, item )
138    case item
139    when AST::Tree
140      look_tree( i, item )
141    when nil
142    else
143      transmit "%s\t%i\t%s", :look, i, serialize_token( item )
144    end
145  end
146  
147  def mark( i )
148    transmit "%s\t%i", :mark, i
149  end
150  
151  def rewind( i = nil )
152    i ? transmit( "%s\t%i", :rewind, i ) : transmit( '%s', :rewind )
153  end
154  
155  def begin_backtrack( level )
156    transmit "%s\t%i", :begin_backtrack, level
157  end
158  def end_backtrack( level, successful )
159    transmit "%s\t%i\t%p", :end_backtrack, level, ( successful ? true : false )
160  end
161  
162  def location( line, position )
163    transmit "%s\t%i\t%i", :location, line, position
164  end
165  
166  def recognition_exception( exception )
167    transmit "%s\t%p\t%i\t%i\t%i", :recognition_exception, exception.class,
168      exception.index, exception.line, exception.column
169  end
170  
171  def begin_resync
172    transmit '%s', :begin_resync
173  end
174  
175  def end_resync
176    transmit '%s', :end_resync
177  end
178  
179  def semantic_predicate( result, predicate )
180    pure_boolean = !( !result )
181    transmit "%s\t%s\t%s", :semantic_predicate, pure_boolean, escape_newlines( predicate )
182  end
183  
184  def consume_node( tree )
185    transmit "%s\t%s", :consume_node, serialize_node( tree )
186  end
187  
188  def adaptor
189    @adaptor ||= ANTLR3::CommonTreeAdaptor.new
190  end
191  
192  def look_tree( i, tree )
193    transmit "%s\t%s\t%s", :look_tree, i, serialize_node( tree )
194  end
195  
196  def flat_node( tree )
197    transmit "%s\t%i", :flat_node, adaptor.unique_id( tree )
198  end
199  
200  def error_node( tree )
201    transmit "%s\t%i\t%i\t%p", :error_node, adaptor.unique_id( tree ),
202            Token::INVALID_TOKEN_TYPE, escape_newlines( tree.to_s )
203  end
204  
205  def create_node( node, token = nil )
206    if token
207      transmit "%s\t%i\t%i", :create_node, adaptor.unique_id( node ),
208              token.token_index
209    else
210      transmit "%s\t%i\t%i\t%p", :create_node, adaptor.unique_id( node ),
211          adaptor.type_of( node ), adaptor.text_of( node )
212    end
213  end
214  
215  def become_root( new_root, old_root )
216    transmit "%s\t%i\t%i", :become_root, adaptor.unique_id( new_root ),
217              adaptor.unique_id( old_root )
218  end
219  
220  def add_child( root, child )
221    transmit "%s\t%i\t%i", :add_child, adaptor.unique_id( root ),
222             adaptor.unique_id( child )
223  end
224  
225  def set_token_boundaries( t, token_start_index, token_stop_index )
226    transmit "%s\t%i\t%i\t%i", :set_token_boundaries, adaptor.unique_id( t ),
227                               token_start_index, token_stop_index
228  end
229  
230  attr_accessor :adaptor
231  
232  def serialize_token( token )
233    [ token.token_index, token.type, token.channel,
234     token.line, token.column,
235     escape_newlines( token.text ) ].join( "\t" )
236  end
237  
238  def serialize_node( node )
239    adaptor ||= ANTLR3::AST::CommonTreeAdaptor.new
240    id = adaptor.unique_id( node )
241    type = adaptor.type_of( node )
242    token = adaptor.token( node )
243    line = token.line rescue -1
244    col  = token.column rescue -1
245    index = adaptor.token_start_index( node )
246    [ id, type, line, col, index ].join( "\t" )
247  end
248  
249  
250  def escape_newlines( text )
251    text.inspect.tap do |t|
252      t.gsub!( /%/, '%%' )
253    end
254  end
255end
256
257=begin rdoc ANTLR3::Debug::RemoteEventSocketListener
258
259A debugging event listener which intercepts debug event messages sent by a EventSocketProxy
260over an IP socket.
261
262=end
263class RemoteEventSocketListener < ::Thread
264  autoload :StringIO, 'stringio'
265  ESCAPE_MAP = Hash.new { |h, k| k }
266  ESCAPE_MAP.update( 
267    ?n => ?\n, ?t => ?\t, ?a => ?\a, ?b => ?\b, ?e => ?\e,
268    ?f => ?\f, ?r => ?\r, ?v => ?\v
269  )
270  
271  attr_reader :host, :port
272  
273  def initialize( options = {} )
274    @listener = listener
275    @host = options.fetch( :host, 'localhost' )
276    @port = options.fetch( :port, DEFAULT_PORT )
277    @buffer = StringIO.new
278    super do
279      connect do
280        handshake
281        loop do
282          yield( read_event )
283        end
284      end
285    end
286  end
287  
288private
289  
290  def handshake
291    @version = @socket.readline.split( "\t" )[ -1 ]
292    @grammar_file = @socket.readline.split( "\t" )[ -1 ]
293    ack
294  end
295  
296  def ack
297    @socket.puts( "ack" )
298    @socket.flush
299  end
300  
301  def unpack_event( event )
302    event.nil? and raise( StopIteration )
303    event.chomp!
304    name, *elements = event.split( "\t",-1 )
305    name = name.to_sym
306    name == :terminate and raise StopIteration
307    elements.map! do |elem|
308      elem.empty? and next( nil )
309      case elem
310      when /^\d+$/ then Integer( elem )
311      when /^\d+\.\d+$/ then Float( elem )
312      when /^true$/ then true
313      when /^false$/ then false
314      when /^"(.*)"$/ then parse_string( $1 )
315      end
316    end
317    elements.unshift( name )
318    return( elements )
319  end
320  
321  def read_event
322    event = @socket.readline or raise( StopIteration )
323    ack
324    return unpack_event( event )
325  end
326  
327  def connect
328    TCPSocket.open( @host, @port ) do |socket|
329      @socket = socket
330      yield
331    end
332  end
333  
334  def parse_string( string )
335    @buffer.string = string
336    @buffer.rewind
337    out = ''
338    until @buffer.eof?
339      case c = @buffer.getc
340      when ?\\                    # escape
341        nc = @buffer.getc
342        out << 
343          if nc.between?( ?0, ?9 )  # octal integer
344            @buffer.ungetc( nc )
345            @buffer.read( 3 ).to_i( 8 ).chr
346          elsif nc == ?x
347            @buffer.read( 2 ).to_i( 16 ).chr
348          else
349            ESCAPE_MAP[ nc ]
350          end
351      else
352        out << c
353      end
354    end
355    return out
356  end
357  
358end # class RemoteEventSocketListener
359end # module Debug
360end # module ANTLR3
361