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.BaseRecognizer; 33 import org.antlr.runtime.tree.TreeAdaptor; 34 35 import java.io.*; 36 import java.net.ServerSocket; 37 import java.net.Socket; 38 39 /** A proxy debug event listener that forwards events over a socket to 40 * a debugger (or any other listener) using a simple text-based protocol; 41 * one event per line. ANTLRWorks listens on server socket with a 42 * RemoteDebugEventSocketListener instance. These two objects must therefore 43 * be kept in sync. New events must be handled on both sides of socket. 44 */ 45 public class DebugEventSocketProxy extends BlankDebugEventListener { 46 public static final int DEFAULT_DEBUGGER_PORT = 49100; // was 49153 47 protected int port = DEFAULT_DEBUGGER_PORT; 48 protected ServerSocket serverSocket; 49 protected Socket socket; 50 protected String grammarFileName; 51 protected PrintWriter out; 52 protected BufferedReader in; 53 54 /** Who am i debugging? */ 55 protected BaseRecognizer recognizer; 56 57 /** Almost certainly the recognizer will have adaptor set, but 58 * we don't know how to cast it (Parser or TreeParser) to get 59 * the adaptor field. Must be set with a constructor. :( 60 */ 61 protected TreeAdaptor adaptor; 62 63 public DebugEventSocketProxy(BaseRecognizer recognizer, TreeAdaptor adaptor) { 64 this(recognizer, DEFAULT_DEBUGGER_PORT, adaptor); 65 } 66 67 public DebugEventSocketProxy(BaseRecognizer recognizer, int port, TreeAdaptor adaptor) { 68 this.grammarFileName = recognizer.getGrammarFileName(); 69 this.adaptor = adaptor; 70 this.port = port; 71 } 72 73 public void handshake() throws IOException { 74 if ( serverSocket==null ) { 75 serverSocket = new ServerSocket(port); 76 socket = serverSocket.accept(); 77 socket.setTcpNoDelay(true); 78 OutputStream os = socket.getOutputStream(); 79 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8"); 80 out = new PrintWriter(new BufferedWriter(osw)); 81 InputStream is = socket.getInputStream(); 82 InputStreamReader isr = new InputStreamReader(is, "UTF8"); 83 in = new BufferedReader(isr); 84 out.println("ANTLR "+ DebugEventListener.PROTOCOL_VERSION); 85 out.println("grammar \""+ grammarFileName); 86 out.flush(); 87 ack(); 88 } 89 } 90 91 public void commence() { 92 // don't bother sending event; listener will trigger upon connection 93 } 94 95 public void terminate() { 96 transmit("terminate"); 97 out.close(); 98 try { 99 socket.close(); 100 } 101 catch (IOException ioe) { 102 ioe.printStackTrace(System.err); 103 } 104 } 105 106 protected void ack() { 107 try { 108 in.readLine(); 109 } 110 catch (IOException ioe) { 111 ioe.printStackTrace(System.err); 112 } 113 } 114 115 protected void transmit(String event) { 116 out.println(event); 117 out.flush(); 118 ack(); 119 } 120 121 public void enterRule(String grammarFileName, String ruleName) { 122 transmit("enterRule\t"+grammarFileName+"\t"+ruleName); 123 } 124 125 public void enterAlt(int alt) { 126 transmit("enterAlt\t"+alt); 127 } 128 129 public void exitRule(String grammarFileName, String ruleName) { 130 transmit("exitRule\t"+grammarFileName+"\t"+ruleName); 131 } 132 133 public void enterSubRule(int decisionNumber) { 134 transmit("enterSubRule\t"+decisionNumber); 135 } 136 137 public void exitSubRule(int decisionNumber) { 138 transmit("exitSubRule\t"+decisionNumber); 139 } 140 141 public void enterDecision(int decisionNumber, boolean couldBacktrack) { 142 transmit("enterDecision\t"+decisionNumber+"\t"+couldBacktrack); 143 } 144 145 public void exitDecision(int decisionNumber) { 146 transmit("exitDecision\t"+decisionNumber); 147 } 148 149 public void consumeToken(Token t) { 150 String buf = serializeToken(t); 151 transmit("consumeToken\t"+buf); 152 } 153 154 public void consumeHiddenToken(Token t) { 155 String buf = serializeToken(t); 156 transmit("consumeHiddenToken\t"+buf); 157 } 158 159 public void LT(int i, Token t) { 160 if(t != null) 161 transmit("LT\t"+i+"\t"+serializeToken(t)); 162 } 163 164 public void mark(int i) { 165 transmit("mark\t"+i); 166 } 167 168 public void rewind(int i) { 169 transmit("rewind\t"+i); 170 } 171 172 public void rewind() { 173 transmit("rewind"); 174 } 175 176 public void beginBacktrack(int level) { 177 transmit("beginBacktrack\t"+level); 178 } 179 180 public void endBacktrack(int level, boolean successful) { 181 transmit("endBacktrack\t"+level+"\t"+(successful?TRUE:FALSE)); 182 } 183 184 public void location(int line, int pos) { 185 transmit("location\t"+line+"\t"+pos); 186 } 187 188 public void recognitionException(RecognitionException e) { 189 StringBuffer buf = new StringBuffer(50); 190 buf.append("exception\t"); 191 buf.append(e.getClass().getName()); 192 // dump only the data common to all exceptions for now 193 buf.append("\t"); 194 buf.append(e.index); 195 buf.append("\t"); 196 buf.append(e.line); 197 buf.append("\t"); 198 buf.append(e.charPositionInLine); 199 transmit(buf.toString()); 200 } 201 202 public void beginResync() { 203 transmit("beginResync"); 204 } 205 206 public void endResync() { 207 transmit("endResync"); 208 } 209 210 public void semanticPredicate(boolean result, String predicate) { 211 StringBuffer buf = new StringBuffer(50); 212 buf.append("semanticPredicate\t"); 213 buf.append(result); 214 serializeText(buf, predicate); 215 transmit(buf.toString()); 216 } 217 218 // A S T P a r s i n g E v e n t s 219 220 public void consumeNode(Object t) { 221 StringBuffer buf = new StringBuffer(50); 222 buf.append("consumeNode"); 223 serializeNode(buf, t); 224 transmit(buf.toString()); 225 } 226 227 public void LT(int i, Object t) { 228 int ID = adaptor.getUniqueID(t); 229 String text = adaptor.getText(t); 230 int type = adaptor.getType(t); 231 StringBuffer buf = new StringBuffer(50); 232 buf.append("LN\t"); // lookahead node; distinguish from LT in protocol 233 buf.append(i); 234 serializeNode(buf, t); 235 transmit(buf.toString()); 236 } 237 238 protected void serializeNode(StringBuffer buf, Object t) { 239 int ID = adaptor.getUniqueID(t); 240 String text = adaptor.getText(t); 241 int type = adaptor.getType(t); 242 buf.append("\t"); 243 buf.append(ID); 244 buf.append("\t"); 245 buf.append(type); 246 Token token = adaptor.getToken(t); 247 int line = -1; 248 int pos = -1; 249 if ( token!=null ) { 250 line = token.getLine(); 251 pos = token.getCharPositionInLine(); 252 } 253 buf.append("\t"); 254 buf.append(line); 255 buf.append("\t"); 256 buf.append(pos); 257 int tokenIndex = adaptor.getTokenStartIndex(t); 258 buf.append("\t"); 259 buf.append(tokenIndex); 260 serializeText(buf, text); 261 } 262 263 264 // A S T E v e n t s 265 266 public void nilNode(Object t) { 267 int ID = adaptor.getUniqueID(t); 268 transmit("nilNode\t"+ID); 269 } 270 271 public void errorNode(Object t) { 272 int ID = adaptor.getUniqueID(t); 273 String text = t.toString(); 274 StringBuffer buf = new StringBuffer(50); 275 buf.append("errorNode\t"); 276 buf.append(ID); 277 buf.append("\t"); 278 buf.append(Token.INVALID_TOKEN_TYPE); 279 serializeText(buf, text); 280 transmit(buf.toString()); 281 } 282 283 public void createNode(Object t) { 284 int ID = adaptor.getUniqueID(t); 285 String text = adaptor.getText(t); 286 int type = adaptor.getType(t); 287 StringBuffer buf = new StringBuffer(50); 288 buf.append("createNodeFromTokenElements\t"); 289 buf.append(ID); 290 buf.append("\t"); 291 buf.append(type); 292 serializeText(buf, text); 293 transmit(buf.toString()); 294 } 295 296 public void createNode(Object node, Token token) { 297 int ID = adaptor.getUniqueID(node); 298 int tokenIndex = token.getTokenIndex(); 299 transmit("createNode\t"+ID+"\t"+tokenIndex); 300 } 301 302 public void becomeRoot(Object newRoot, Object oldRoot) { 303 int newRootID = adaptor.getUniqueID(newRoot); 304 int oldRootID = adaptor.getUniqueID(oldRoot); 305 transmit("becomeRoot\t"+newRootID+"\t"+oldRootID); 306 } 307 308 public void addChild(Object root, Object child) { 309 int rootID = adaptor.getUniqueID(root); 310 int childID = adaptor.getUniqueID(child); 311 transmit("addChild\t"+rootID+"\t"+childID); 312 } 313 314 public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) { 315 int ID = adaptor.getUniqueID(t); 316 transmit("setTokenBoundaries\t"+ID+"\t"+tokenStartIndex+"\t"+tokenStopIndex); 317 } 318 319 320 // support 321 322 public void setTreeAdaptor(TreeAdaptor adaptor) { this.adaptor = adaptor; } 323 public TreeAdaptor getTreeAdaptor() { return adaptor; } 324 325 protected String serializeToken(Token t) { 326 StringBuffer buf = new StringBuffer(50); 327 buf.append(t.getTokenIndex()); buf.append('\t'); 328 buf.append(t.getType()); buf.append('\t'); 329 buf.append(t.getChannel()); buf.append('\t'); 330 buf.append(t.getLine()); buf.append('\t'); 331 buf.append(t.getCharPositionInLine()); 332 serializeText(buf, t.getText()); 333 return buf.toString(); 334 } 335 336 protected void serializeText(StringBuffer buf, String text) { 337 buf.append("\t\""); 338 if ( text==null ) { 339 text = ""; 340 } 341 // escape \n and \r all text for token appears to exist on one line 342 // this escape is slow but easy to understand 343 text = escapeNewlines(text); 344 buf.append(text); 345 } 346 347 protected String escapeNewlines(String txt) { 348 txt = txt.replaceAll("%","%25"); // escape all escape char ;) 349 txt = txt.replaceAll("\n","%0A"); // escape \n 350 txt = txt.replaceAll("\r","%0D"); // escape \r 351 return txt; 352 } 353 } 354 355