001: /*
002: [The "BSD licence"]
003: Copyright (c) 2005-2006 Terence Parr
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009: 1. Redistributions of source code must retain the above copyright
010: notice, this list of conditions and the following disclaimer.
011: 2. Redistributions in binary form must reproduce the above copyright
012: notice, this list of conditions and the following disclaimer in the
013: documentation and/or other materials provided with the distribution.
014: 3. The name of the author may not be used to endorse or promote products
015: derived from this software without specific prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
018: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
019: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
020: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
021: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028: package org.antlr.runtime.debug;
029:
030: import org.antlr.runtime.RecognitionException;
031: import org.antlr.runtime.Token;
032: import org.antlr.runtime.BaseRecognizer;
033: import org.antlr.runtime.tree.TreeAdaptor;
034:
035: import java.io.*;
036: import java.net.ServerSocket;
037: import java.net.Socket;
038:
039: /** A proxy debug event listener that forwards events over a socket to
040: * a debugger (or any other listener) using a simple text-based protocol;
041: * one event per line. ANTLRWorks listens on server socket with a
042: * RemoteDebugEventSocketListener instance. These two objects must therefore
043: * be kept in sync. New events must be handled on both sides of socket.
044: */
045: public class DebugEventSocketProxy extends BlankDebugEventListener {
046: public static final int DEFAULT_DEBUGGER_PORT = 0xC001;
047: protected int port = DEFAULT_DEBUGGER_PORT;
048: protected ServerSocket serverSocket;
049: protected Socket socket;
050: protected String grammarFileName;
051: protected PrintWriter out;
052: protected BufferedReader in;
053:
054: /** Who am i debugging? */
055: protected BaseRecognizer recognizer;
056:
057: /** Almost certainly the recognizer will have adaptor set, but
058: * we don't know how to cast it (Parser or TreeParser) to get
059: * the adaptor field. Must be set with a constructor. :(
060: */
061: protected TreeAdaptor adaptor;
062:
063: public DebugEventSocketProxy(BaseRecognizer recognizer,
064: TreeAdaptor adaptor) {
065: this (recognizer, DEFAULT_DEBUGGER_PORT, adaptor);
066: }
067:
068: public DebugEventSocketProxy(BaseRecognizer recognizer, int port,
069: TreeAdaptor adaptor) {
070: this .grammarFileName = recognizer.getGrammarFileName();
071: this .adaptor = adaptor;
072: this .port = port;
073: }
074:
075: public void handshake() throws IOException {
076: if (serverSocket == null) {
077: serverSocket = new ServerSocket(port);
078: socket = serverSocket.accept();
079: socket.setTcpNoDelay(true);
080: OutputStream os = socket.getOutputStream();
081: OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
082: out = new PrintWriter(new BufferedWriter(osw));
083: InputStream is = socket.getInputStream();
084: InputStreamReader isr = new InputStreamReader(is, "UTF8");
085: in = new BufferedReader(isr);
086: out.println("ANTLR " + DebugEventListener.PROTOCOL_VERSION);
087: out.println("grammar \"" + grammarFileName);
088: out.flush();
089: }
090: }
091:
092: public void commence() {
093: // don't bother sending event; listener will trigger upon connection
094: }
095:
096: public void terminate() {
097: transmit("terminate");
098: out.close();
099: try {
100: socket.close();
101: } catch (IOException ioe) {
102: ioe.printStackTrace(System.err);
103: }
104: }
105:
106: protected void ack() {
107: try {
108: in.readLine();
109: } catch (IOException ioe) {
110: ioe.printStackTrace(System.err);
111: }
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 ruleName) {
122: transmit("enterRule " + ruleName);
123: }
124:
125: public void enterAlt(int alt) {
126: transmit("enterAlt " + alt);
127: }
128:
129: public void exitRule(String ruleName) {
130: transmit("exitRule " + ruleName);
131: }
132:
133: public void enterSubRule(int decisionNumber) {
134: transmit("enterSubRule " + decisionNumber);
135: }
136:
137: public void exitSubRule(int decisionNumber) {
138: transmit("exitSubRule " + decisionNumber);
139: }
140:
141: public void enterDecision(int decisionNumber) {
142: transmit("enterDecision " + decisionNumber);
143: }
144:
145: public void exitDecision(int decisionNumber) {
146: transmit("exitDecision " + decisionNumber);
147: }
148:
149: public void consumeToken(Token t) {
150: String buf = serializeToken(t);
151: transmit("consumeToken " + buf);
152: }
153:
154: public void consumeHiddenToken(Token t) {
155: String buf = serializeToken(t);
156: transmit("consumeHiddenToken " + buf);
157: }
158:
159: public void LT(int i, Token t) {
160: if (t != null)
161: transmit("LT " + i + " " + serializeToken(t));
162: }
163:
164: public void mark(int i) {
165: transmit("mark " + i);
166: }
167:
168: public void rewind(int i) {
169: transmit("rewind " + i);
170: }
171:
172: public void rewind() {
173: transmit("rewind");
174: }
175:
176: public void beginBacktrack(int level) {
177: transmit("beginBacktrack " + level);
178: }
179:
180: public void endBacktrack(int level, boolean successful) {
181: transmit("endBacktrack " + level + " "
182: + (successful ? TRUE : FALSE));
183: }
184:
185: public void location(int line, int pos) {
186: transmit("location " + line + " " + pos);
187: }
188:
189: public void recognitionException(RecognitionException e) {
190: StringBuffer buf = new StringBuffer(50);
191: buf.append("exception ");
192: buf.append(e.getClass().getName());
193: // dump only the data common to all exceptions for now
194: buf.append(" ");
195: buf.append(e.index);
196: buf.append(" ");
197: buf.append(e.line);
198: buf.append(" ");
199: buf.append(e.charPositionInLine);
200: transmit(buf.toString());
201: }
202:
203: public void beginResync() {
204: transmit("beginResync");
205: }
206:
207: public void endResync() {
208: transmit("endResync");
209: }
210:
211: public void semanticPredicate(boolean result, String predicate) {
212: StringBuffer buf = new StringBuffer(50);
213: buf.append("semanticPredicate ");
214: buf.append(result);
215: serializeText(buf, predicate);
216: transmit(buf.toString());
217: }
218:
219: // A S T P a r s i n g E v e n t s
220:
221: public void consumeNode(Object t) {
222: StringBuffer buf = new StringBuffer(50);
223: buf.append("consumeNode");
224: serializeNode(buf, t);
225: transmit(buf.toString());
226: }
227:
228: public void LT(int i, Object t) {
229: int ID = adaptor.getUniqueID(t);
230: String text = adaptor.getText(t);
231: int type = adaptor.getType(t);
232: StringBuffer buf = new StringBuffer(50);
233: buf.append("LN "); // lookahead node; distinguish from LT in protocol
234: buf.append(i);
235: serializeNode(buf, t);
236: transmit(buf.toString());
237: }
238:
239: protected void serializeNode(StringBuffer buf, Object t) {
240: int ID = adaptor.getUniqueID(t);
241: String text = adaptor.getText(t);
242: int type = adaptor.getType(t);
243: buf.append(" ");
244: buf.append(ID);
245: buf.append(" ");
246: buf.append(type);
247: Token token = adaptor.getToken(t);
248: int line = -1;
249: int pos = -1;
250: if (token != null) {
251: line = token.getLine();
252: pos = token.getCharPositionInLine();
253: }
254: buf.append(" ");
255: buf.append(line);
256: buf.append(" ");
257: buf.append(pos);
258: int tokenIndex = adaptor.getTokenStartIndex(t);
259: buf.append(" ");
260: buf.append(tokenIndex);
261: serializeText(buf, text);
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 " + ID);
269: }
270:
271: public void createNode(Object t) {
272: int ID = adaptor.getUniqueID(t);
273: String text = adaptor.getText(t);
274: int type = adaptor.getType(t);
275: StringBuffer buf = new StringBuffer(50);
276: buf.append("createNodeFromTokenElements ");
277: buf.append(ID);
278: buf.append(" ");
279: buf.append(type);
280: serializeText(buf, text);
281: transmit(buf.toString());
282: }
283:
284: public void createNode(Object node, Token token) {
285: int ID = adaptor.getUniqueID(node);
286: int tokenIndex = token.getTokenIndex();
287: transmit("createNode " + ID + " " + tokenIndex);
288: }
289:
290: public void becomeRoot(Object newRoot, Object oldRoot) {
291: int newRootID = adaptor.getUniqueID(newRoot);
292: int oldRootID = adaptor.getUniqueID(oldRoot);
293: transmit("becomeRoot " + newRootID + " " + oldRootID);
294: }
295:
296: public void addChild(Object root, Object child) {
297: int rootID = adaptor.getUniqueID(root);
298: int childID = adaptor.getUniqueID(child);
299: transmit("addChild " + rootID + " " + childID);
300: }
301:
302: public void setTokenBoundaries(Object t, int tokenStartIndex,
303: int tokenStopIndex) {
304: int ID = adaptor.getUniqueID(t);
305: transmit("setTokenBoundaries " + ID + " " + tokenStartIndex
306: + " " + tokenStopIndex);
307: }
308:
309: // support
310:
311: protected String serializeToken(Token t) {
312: StringBuffer buf = new StringBuffer(50);
313: buf.append(t.getTokenIndex());
314: buf.append(' ');
315: buf.append(t.getType());
316: buf.append(' ');
317: buf.append(t.getChannel());
318: buf.append(' ');
319: buf.append(t.getLine());
320: buf.append(' ');
321: buf.append(t.getCharPositionInLine());
322: serializeText(buf, t.getText());
323: return buf.toString();
324: }
325:
326: protected void serializeText(StringBuffer buf, String text) {
327: buf.append(" \"");
328: if (text == null) {
329: text = "";
330: }
331: // escape \n and \r all text for token appears to exist on one line
332: // this escape is slow but easy to understand
333: text = escapeNewlines(text);
334: buf.append(text);
335: }
336:
337: protected String escapeNewlines(String txt) {
338: txt = txt.replaceAll("%", "%25"); // escape all escape char ;)
339: txt = txt.replaceAll("\n", "%0A"); // escape \n
340: txt = txt.replaceAll("\r", "%0D"); // escape \r
341: return txt;
342: }
343: }
|