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