Home | History | Annotate | Download | only in debug
      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