1 /* 2 [The "BSD license"] 3 Copyright (c) 2005-2009 Terence Parr 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 1. Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 3. The name of the author may not be used to endorse or promote products 15 derived from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 package org.antlr.runtime.debug; 29 30 import org.antlr.runtime.RecognitionException; 31 import org.antlr.runtime.Token; 32 import org.antlr.runtime.CharStream; 33 import org.antlr.runtime.tree.BaseTree; 34 import org.antlr.runtime.tree.Tree; 35 36 import java.io.*; 37 import java.net.ConnectException; 38 import java.net.Socket; 39 import java.util.StringTokenizer; 40 41 public class RemoteDebugEventSocketListener implements Runnable { 42 static final int MAX_EVENT_ELEMENTS = 8; 43 DebugEventListener listener; 44 String machine; 45 int port; 46 Socket channel = null; 47 PrintWriter out; 48 BufferedReader in; 49 String event; 50 /** Version of ANTLR (dictates events) */ 51 public String version; 52 public String grammarFileName; 53 /** Track the last token index we saw during a consume. If same, then 54 * set a flag that we have a problem. 55 */ 56 int previousTokenIndex = -1; 57 boolean tokenIndexesInvalid = false; 58 59 public static class ProxyToken implements Token { 60 int index; 61 int type; 62 int channel; 63 int line; 64 int charPos; 65 String text; 66 public ProxyToken(int index) { this.index = index; } 67 public ProxyToken(int index, int type, int channel, 68 int line, int charPos, String text) 69 { 70 this.index = index; 71 this.type = type; 72 this.channel = channel; 73 this.line = line; 74 this.charPos = charPos; 75 this.text = text; 76 } 77 public String getText() { 78 return text; 79 } 80 public void setText(String text) { 81 this.text = text; 82 } 83 public int getType() { 84 return type; 85 } 86 public void setType(int ttype) { 87 this.type = ttype; 88 } 89 public int getLine() { 90 return line; 91 } 92 public void setLine(int line) { 93 this.line = line; 94 } 95 public int getCharPositionInLine() { 96 return charPos; 97 } 98 public void setCharPositionInLine(int pos) { 99 this.charPos = pos; 100 } 101 public int getChannel() { 102 return channel; 103 } 104 public void setChannel(int channel) { 105 this.channel = channel; 106 } 107 public int getTokenIndex() { 108 return index; 109 } 110 public void setTokenIndex(int index) { 111 this.index = index; 112 } 113 public CharStream getInputStream() { 114 return null; 115 } 116 public void setInputStream(CharStream input) { 117 } 118 public String toString() { 119 String channelStr = ""; 120 if ( channel!=Token.DEFAULT_CHANNEL ) { 121 channelStr=",channel="+channel; 122 } 123 return "["+getText()+"/<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+",@"+index+"]"; 124 } 125 } 126 127 public static class ProxyTree extends BaseTree { 128 public int ID; 129 public int type; 130 public int line = 0; 131 public int charPos = -1; 132 public int tokenIndex = -1; 133 public String text; 134 135 public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text) { 136 this.ID = ID; 137 this.type = type; 138 this.line = line; 139 this.charPos = charPos; 140 this.tokenIndex = tokenIndex; 141 this.text = text; 142 } 143 144 public ProxyTree(int ID) { this.ID = ID; } 145 146 public int getTokenStartIndex() { return tokenIndex; } 147 public void setTokenStartIndex(int index) { } 148 public int getTokenStopIndex() { return 0; } 149 public void setTokenStopIndex(int index) { } 150 public Tree dupNode() { return null; } 151 public int getType() { return type; } 152 public String getText() { return text; } 153 public String toString() { 154 return "fix this"; 155 } 156 } 157 158 public RemoteDebugEventSocketListener(DebugEventListener listener, 159 String machine, 160 int port) throws IOException 161 { 162 this.listener = listener; 163 this.machine = machine; 164 this.port = port; 165 166 if( !openConnection() ) { 167 throw new ConnectException(); 168 } 169 } 170 171 protected void eventHandler() { 172 try { 173 handshake(); 174 event = in.readLine(); 175 while ( event!=null ) { 176 dispatch(event); 177 ack(); 178 event = in.readLine(); 179 } 180 } 181 catch (Exception e) { 182 System.err.println(e); 183 e.printStackTrace(System.err); 184 } 185 finally { 186 closeConnection(); 187 } 188 } 189 190 protected boolean openConnection() { 191 boolean success = false; 192 try { 193 channel = new Socket(machine, port); 194 channel.setTcpNoDelay(true); 195 OutputStream os = channel.getOutputStream(); 196 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8"); 197 out = new PrintWriter(new BufferedWriter(osw)); 198 InputStream is = channel.getInputStream(); 199 InputStreamReader isr = new InputStreamReader(is, "UTF8"); 200 in = new BufferedReader(isr); 201 success = true; 202 } catch(Exception e) { 203 System.err.println(e); 204 } 205 return success; 206 } 207 208 protected void closeConnection() { 209 try { 210 in.close(); in = null; 211 out.close(); out = null; 212 channel.close(); channel=null; 213 } 214 catch (Exception e) { 215 System.err.println(e); 216 e.printStackTrace(System.err); 217 } 218 finally { 219 if ( in!=null ) { 220 try {in.close();} catch (IOException ioe) { 221 System.err.println(ioe); 222 } 223 } 224 if ( out!=null ) { 225 out.close(); 226 } 227 if ( channel!=null ) { 228 try {channel.close();} catch (IOException ioe) { 229 System.err.println(ioe); 230 } 231 } 232 } 233 234 } 235 236 protected void handshake() throws IOException { 237 String antlrLine = in.readLine(); 238 String[] antlrElements = getEventElements(antlrLine); 239 version = antlrElements[1]; 240 String grammarLine = in.readLine(); 241 String[] grammarElements = getEventElements(grammarLine); 242 grammarFileName = grammarElements[1]; 243 ack(); 244 listener.commence(); // inform listener after handshake 245 } 246 247 protected void ack() { 248 out.println("ack"); 249 out.flush(); 250 } 251 252 protected void dispatch(String line) { 253 //System.out.println("event: "+line); 254 String[] elements = getEventElements(line); 255 if ( elements==null || elements[0]==null ) { 256 System.err.println("unknown debug event: "+line); 257 return; 258 } 259 if ( elements[0].equals("enterRule") ) { 260 listener.enterRule(elements[1], elements[2]); 261 } 262 else if ( elements[0].equals("exitRule") ) { 263 listener.exitRule(elements[1], elements[2]); 264 } 265 else if ( elements[0].equals("enterAlt") ) { 266 listener.enterAlt(Integer.parseInt(elements[1])); 267 } 268 else if ( elements[0].equals("enterSubRule") ) { 269 listener.enterSubRule(Integer.parseInt(elements[1])); 270 } 271 else if ( elements[0].equals("exitSubRule") ) { 272 listener.exitSubRule(Integer.parseInt(elements[1])); 273 } 274 else if ( elements[0].equals("enterDecision") ) { 275 listener.enterDecision(Integer.parseInt(elements[1]), elements[2].equals("true")); 276 } 277 else if ( elements[0].equals("exitDecision") ) { 278 listener.exitDecision(Integer.parseInt(elements[1])); 279 } 280 else if ( elements[0].equals("location") ) { 281 listener.location(Integer.parseInt(elements[1]), 282 Integer.parseInt(elements[2])); 283 } 284 else if ( elements[0].equals("consumeToken") ) { 285 ProxyToken t = deserializeToken(elements, 1); 286 if ( t.getTokenIndex() == previousTokenIndex ) { 287 tokenIndexesInvalid = true; 288 } 289 previousTokenIndex = t.getTokenIndex(); 290 listener.consumeToken(t); 291 } 292 else if ( elements[0].equals("consumeHiddenToken") ) { 293 ProxyToken t = deserializeToken(elements, 1); 294 if ( t.getTokenIndex() == previousTokenIndex ) { 295 tokenIndexesInvalid = true; 296 } 297 previousTokenIndex = t.getTokenIndex(); 298 listener.consumeHiddenToken(t); 299 } 300 else if ( elements[0].equals("LT") ) { 301 Token t = deserializeToken(elements, 2); 302 listener.LT(Integer.parseInt(elements[1]), t); 303 } 304 else if ( elements[0].equals("mark") ) { 305 listener.mark(Integer.parseInt(elements[1])); 306 } 307 else if ( elements[0].equals("rewind") ) { 308 if ( elements[1]!=null ) { 309 listener.rewind(Integer.parseInt(elements[1])); 310 } 311 else { 312 listener.rewind(); 313 } 314 } 315 else if ( elements[0].equals("beginBacktrack") ) { 316 listener.beginBacktrack(Integer.parseInt(elements[1])); 317 } 318 else if ( elements[0].equals("endBacktrack") ) { 319 int level = Integer.parseInt(elements[1]); 320 int successI = Integer.parseInt(elements[2]); 321 listener.endBacktrack(level, successI==DebugEventListener.TRUE); 322 } 323 else if ( elements[0].equals("exception") ) { 324 String excName = elements[1]; 325 String indexS = elements[2]; 326 String lineS = elements[3]; 327 String posS = elements[4]; 328 Class excClass = null; 329 try { 330 excClass = Class.forName(excName); 331 RecognitionException e = 332 (RecognitionException)excClass.newInstance(); 333 e.index = Integer.parseInt(indexS); 334 e.line = Integer.parseInt(lineS); 335 e.charPositionInLine = Integer.parseInt(posS); 336 listener.recognitionException(e); 337 } 338 catch (ClassNotFoundException cnfe) { 339 System.err.println("can't find class "+cnfe); 340 cnfe.printStackTrace(System.err); 341 } 342 catch (InstantiationException ie) { 343 System.err.println("can't instantiate class "+ie); 344 ie.printStackTrace(System.err); 345 } 346 catch (IllegalAccessException iae) { 347 System.err.println("can't access class "+iae); 348 iae.printStackTrace(System.err); 349 } 350 } 351 else if ( elements[0].equals("beginResync") ) { 352 listener.beginResync(); 353 } 354 else if ( elements[0].equals("endResync") ) { 355 listener.endResync(); 356 } 357 else if ( elements[0].equals("terminate") ) { 358 listener.terminate(); 359 } 360 else if ( elements[0].equals("semanticPredicate") ) { 361 Boolean result = Boolean.valueOf(elements[1]); 362 String predicateText = elements[2]; 363 predicateText = unEscapeNewlines(predicateText); 364 listener.semanticPredicate(result.booleanValue(), 365 predicateText); 366 } 367 else if ( elements[0].equals("consumeNode") ) { 368 ProxyTree node = deserializeNode(elements, 1); 369 listener.consumeNode(node); 370 } 371 else if ( elements[0].equals("LN") ) { 372 int i = Integer.parseInt(elements[1]); 373 ProxyTree node = deserializeNode(elements, 2); 374 listener.LT(i, node); 375 } 376 else if ( elements[0].equals("createNodeFromTokenElements") ) { 377 int ID = Integer.parseInt(elements[1]); 378 int type = Integer.parseInt(elements[2]); 379 String text = elements[3]; 380 text = unEscapeNewlines(text); 381 ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); 382 listener.createNode(node); 383 } 384 else if ( elements[0].equals("createNode") ) { 385 int ID = Integer.parseInt(elements[1]); 386 int tokenIndex = Integer.parseInt(elements[2]); 387 // create dummy node/token filled with ID, tokenIndex 388 ProxyTree node = new ProxyTree(ID); 389 ProxyToken token = new ProxyToken(tokenIndex); 390 listener.createNode(node, token); 391 } 392 else if ( elements[0].equals("nilNode") ) { 393 int ID = Integer.parseInt(elements[1]); 394 ProxyTree node = new ProxyTree(ID); 395 listener.nilNode(node); 396 } 397 else if ( elements[0].equals("errorNode") ) { 398 // TODO: do we need a special tree here? 399 int ID = Integer.parseInt(elements[1]); 400 int type = Integer.parseInt(elements[2]); 401 String text = elements[3]; 402 text = unEscapeNewlines(text); 403 ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); 404 listener.errorNode(node); 405 } 406 else if ( elements[0].equals("becomeRoot") ) { 407 int newRootID = Integer.parseInt(elements[1]); 408 int oldRootID = Integer.parseInt(elements[2]); 409 ProxyTree newRoot = new ProxyTree(newRootID); 410 ProxyTree oldRoot = new ProxyTree(oldRootID); 411 listener.becomeRoot(newRoot, oldRoot); 412 } 413 else if ( elements[0].equals("addChild") ) { 414 int rootID = Integer.parseInt(elements[1]); 415 int childID = Integer.parseInt(elements[2]); 416 ProxyTree root = new ProxyTree(rootID); 417 ProxyTree child = new ProxyTree(childID); 418 listener.addChild(root, child); 419 } 420 else if ( elements[0].equals("setTokenBoundaries") ) { 421 int ID = Integer.parseInt(elements[1]); 422 ProxyTree node = new ProxyTree(ID); 423 listener.setTokenBoundaries( 424 node, 425 Integer.parseInt(elements[2]), 426 Integer.parseInt(elements[3])); 427 } 428 else { 429 System.err.println("unknown debug event: "+line); 430 } 431 } 432 433 protected ProxyTree deserializeNode(String[] elements, int offset) { 434 int ID = Integer.parseInt(elements[offset+0]); 435 int type = Integer.parseInt(elements[offset+1]); 436 int tokenLine = Integer.parseInt(elements[offset+2]); 437 int charPositionInLine = Integer.parseInt(elements[offset+3]); 438 int tokenIndex = Integer.parseInt(elements[offset+4]); 439 String text = elements[offset+5]; 440 text = unEscapeNewlines(text); 441 return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text); 442 } 443 444 protected ProxyToken deserializeToken(String[] elements, 445 int offset) 446 { 447 String indexS = elements[offset+0]; 448 String typeS = elements[offset+1]; 449 String channelS = elements[offset+2]; 450 String lineS = elements[offset+3]; 451 String posS = elements[offset+4]; 452 String text = elements[offset+5]; 453 text = unEscapeNewlines(text); 454 int index = Integer.parseInt(indexS); 455 ProxyToken t = 456 new ProxyToken(index, 457 Integer.parseInt(typeS), 458 Integer.parseInt(channelS), 459 Integer.parseInt(lineS), 460 Integer.parseInt(posS), 461 text); 462 return t; 463 } 464 465 /** Create a thread to listen to the remote running recognizer */ 466 public void start() { 467 Thread t = new Thread(this); 468 t.start(); 469 } 470 471 public void run() { 472 eventHandler(); 473 } 474 475 // M i s c 476 477 public String[] getEventElements(String event) { 478 if ( event==null ) { 479 return null; 480 } 481 String[] elements = new String[MAX_EVENT_ELEMENTS]; 482 String str = null; // a string element if present (must be last) 483 try { 484 int firstQuoteIndex = event.indexOf('"'); 485 if ( firstQuoteIndex>=0 ) { 486 // treat specially; has a string argument like "a comment\n 487 // Note that the string is terminated by \n not end quote. 488 // Easier to parse that way. 489 String eventWithoutString = event.substring(0,firstQuoteIndex); 490 str = event.substring(firstQuoteIndex+1,event.length()); 491 event = eventWithoutString; 492 } 493 StringTokenizer st = new StringTokenizer(event, "\t", false); 494 int i = 0; 495 while ( st.hasMoreTokens() ) { 496 if ( i>=MAX_EVENT_ELEMENTS ) { 497 // ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event); 498 return elements; 499 } 500 elements[i] = st.nextToken(); 501 i++; 502 } 503 if ( str!=null ) { 504 elements[i] = str; 505 } 506 } 507 catch (Exception e) { 508 e.printStackTrace(System.err); 509 } 510 return elements; 511 } 512 513 protected String unEscapeNewlines(String txt) { 514 // this unescape is slow but easy to understand 515 txt = txt.replaceAll("%0A","\n"); // unescape \n 516 txt = txt.replaceAll("%0D","\r"); // unescape \r 517 txt = txt.replaceAll("%25","%"); // undo escaped escape chars 518 return txt; 519 } 520 521 public boolean tokenIndexesAreInvalid() { 522 return false; 523 //return tokenIndexesInvalid; 524 } 525 526 } 527 528