Home | History | Annotate | Download | only in debug
      1 #!/usr/bin/ruby
      2 # encoding: utf-8
      3 
      4 require 'socket'
      5 
      6 module ANTLR3
      7 module Debug
      8 
      9 
     10 =begin rdoc ANTLR3::Debug::EventSocketProxy
     11 
     12 A proxy debug event listener that forwards events over a socket to
     13 a debugger (or any other listener) using a simple text-based protocol;
     14 one event per line.  ANTLRWorks listens on server socket with a
     15 RemoteDebugEventSocketListener instance.  These two objects must therefore
     16 be kept in sync.  New events must be handled on both sides of socket.
     17 
     18 =end
     19 class 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
    255 end
    256 
    257 =begin rdoc ANTLR3::Debug::RemoteEventSocketListener
    258 
    259 A debugging event listener which intercepts debug event messages sent by a EventSocketProxy
    260 over an IP socket.
    261 
    262 =end
    263 class 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   
    288 private
    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   
    358 end # class RemoteEventSocketListener
    359 end # module Debug
    360 end # module ANTLR3
    361