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.tree.BaseTree;
033: import org.antlr.runtime.tree.Tree;
034:
035: import java.io.*;
036: import java.net.ConnectException;
037: import java.net.Socket;
038: import java.util.StringTokenizer;
039:
040: public class RemoteDebugEventSocketListener implements Runnable {
041: static final int MAX_EVENT_ELEMENTS = 8;
042: DebugEventListener listener;
043: String machine;
044: int port;
045: Socket channel = null;
046: PrintWriter out;
047: BufferedReader in;
048: String event;
049: /** Version of ANTLR (dictates events) */
050: public String version;
051: public String grammarFileName;
052: /** Track the last token index we saw during a consume. If same, then
053: * set a flag that we have a problem.
054: */
055: int previousTokenIndex = -1;
056: boolean tokenIndexesInvalid = false;
057:
058: public static class ProxyToken implements Token {
059: int index;
060: int type;
061: int channel;
062: int line;
063: int charPos;
064: String text;
065:
066: public ProxyToken(int index) {
067: this .index = index;
068: }
069:
070: public ProxyToken(int index, int type, int channel, int line,
071: int charPos, String text) {
072: this .index = index;
073: this .type = type;
074: this .channel = channel;
075: this .line = line;
076: this .charPos = charPos;
077: this .text = text;
078: }
079:
080: public String getText() {
081: return text;
082: }
083:
084: public void setText(String text) {
085: this .text = text;
086: }
087:
088: public int getType() {
089: return type;
090: }
091:
092: public void setType(int ttype) {
093: this .type = ttype;
094: }
095:
096: public int getLine() {
097: return line;
098: }
099:
100: public void setLine(int line) {
101: this .line = line;
102: }
103:
104: public int getCharPositionInLine() {
105: return charPos;
106: }
107:
108: public void setCharPositionInLine(int pos) {
109: this .charPos = pos;
110: }
111:
112: public int getChannel() {
113: return channel;
114: }
115:
116: public void setChannel(int channel) {
117: this .channel = channel;
118: }
119:
120: public int getTokenIndex() {
121: return index;
122: }
123:
124: public void setTokenIndex(int index) {
125: this .index = index;
126: }
127:
128: public String toString() {
129: String channelStr = "";
130: if (channel != Token.DEFAULT_CHANNEL) {
131: channelStr = ",channel=" + channel;
132: }
133: return "[" + getText() + "/<" + type + ">" + channelStr
134: + "," + line + ":" + getCharPositionInLine() + ",@"
135: + index + "]";
136: }
137: }
138:
139: public static class ProxyTree extends BaseTree {
140: public int ID;
141: public int type;
142: public int line = 0;
143: public int charPos = -1;
144: public int tokenIndex = -1;
145: public String text;
146:
147: public ProxyTree(int ID, int type, int line, int charPos,
148: int tokenIndex, String text) {
149: this .ID = ID;
150: this .type = type;
151: this .line = line;
152: this .charPos = charPos;
153: this .tokenIndex = tokenIndex;
154: this .text = text;
155: }
156:
157: public ProxyTree(int ID) {
158: this .ID = ID;
159: }
160:
161: public int getTokenStartIndex() {
162: return tokenIndex;
163: }
164:
165: public void setTokenStartIndex(int index) {
166: }
167:
168: public int getTokenStopIndex() {
169: return 0;
170: }
171:
172: public void setTokenStopIndex(int index) {
173: }
174:
175: public Tree dupNode() {
176: return null;
177: }
178:
179: public int getType() {
180: return type;
181: }
182:
183: public String getText() {
184: return text;
185: }
186:
187: public String toString() {
188: return "fix this";
189: }
190: }
191:
192: public RemoteDebugEventSocketListener(DebugEventListener listener,
193: String machine, int port) throws IOException {
194: this .listener = listener;
195: this .machine = machine;
196: this .port = port;
197:
198: if (!openConnection()) {
199: throw new ConnectException();
200: }
201: }
202:
203: protected void eventHandler() {
204: try {
205: handshake();
206: event = in.readLine();
207: while (event != null) {
208: dispatch(event);
209: ack();
210: event = in.readLine();
211: }
212: } catch (Exception e) {
213: System.err.println(e);
214: e.printStackTrace(System.err);
215: } finally {
216: closeConnection();
217: }
218: }
219:
220: protected boolean openConnection() {
221: boolean success = false;
222: try {
223: channel = new Socket(machine, port);
224: channel.setTcpNoDelay(true);
225: OutputStream os = channel.getOutputStream();
226: OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
227: out = new PrintWriter(new BufferedWriter(osw));
228: InputStream is = channel.getInputStream();
229: InputStreamReader isr = new InputStreamReader(is, "UTF8");
230: in = new BufferedReader(isr);
231: success = true;
232: } catch (Exception e) {
233: System.err.println(e);
234: }
235: return success;
236: }
237:
238: protected void closeConnection() {
239: try {
240: in.close();
241: in = null;
242: out.close();
243: out = null;
244: channel.close();
245: channel = null;
246: } catch (Exception e) {
247: System.err.println(e);
248: e.printStackTrace(System.err);
249: } finally {
250: if (in != null) {
251: try {
252: in.close();
253: } catch (IOException ioe) {
254: System.err.println(ioe);
255: }
256: }
257: if (out != null) {
258: out.close();
259: }
260: if (channel != null) {
261: try {
262: channel.close();
263: } catch (IOException ioe) {
264: System.err.println(ioe);
265: }
266: }
267: }
268:
269: }
270:
271: protected void handshake() throws IOException {
272: String antlrLine = in.readLine();
273: String[] antlrElements = getEventElements(antlrLine);
274: version = antlrElements[1];
275: String grammarLine = in.readLine();
276: String[] grammarElements = getEventElements(grammarLine);
277: grammarFileName = grammarElements[1];
278: ack();
279: listener.commence(); // inform listener after handshake
280: }
281:
282: protected void ack() {
283: out.println("ack");
284: out.flush();
285: }
286:
287: protected void dispatch(String line) {
288: String[] elements = getEventElements(line);
289: if (elements == null || elements[0] == null) {
290: System.err.println("unknown debug event: " + line);
291: return;
292: }
293: if (elements[0].equals("enterRule")) {
294: listener.enterRule(elements[1]);
295: } else if (elements[0].equals("exitRule")) {
296: listener.exitRule(elements[1]);
297: } else if (elements[0].equals("enterAlt")) {
298: listener.enterAlt(Integer.parseInt(elements[1]));
299: } else if (elements[0].equals("enterSubRule")) {
300: listener.enterSubRule(Integer.parseInt(elements[1]));
301: } else if (elements[0].equals("exitSubRule")) {
302: listener.exitSubRule(Integer.parseInt(elements[1]));
303: } else if (elements[0].equals("enterDecision")) {
304: listener.enterDecision(Integer.parseInt(elements[1]));
305: } else if (elements[0].equals("exitDecision")) {
306: listener.exitDecision(Integer.parseInt(elements[1]));
307: } else if (elements[0].equals("location")) {
308: listener.location(Integer.parseInt(elements[1]), Integer
309: .parseInt(elements[2]));
310: } else if (elements[0].equals("consumeToken")) {
311: ProxyToken t = deserializeToken(elements, 1);
312: if (t.getTokenIndex() == previousTokenIndex) {
313: tokenIndexesInvalid = true;
314: }
315: previousTokenIndex = t.getTokenIndex();
316: listener.consumeToken(t);
317: } else if (elements[0].equals("consumeHiddenToken")) {
318: ProxyToken t = deserializeToken(elements, 1);
319: if (t.getTokenIndex() == previousTokenIndex) {
320: tokenIndexesInvalid = true;
321: }
322: previousTokenIndex = t.getTokenIndex();
323: listener.consumeHiddenToken(t);
324: } else if (elements[0].equals("LT")) {
325: Token t = deserializeToken(elements, 2);
326: listener.LT(Integer.parseInt(elements[1]), t);
327: } else if (elements[0].equals("mark")) {
328: listener.mark(Integer.parseInt(elements[1]));
329: } else if (elements[0].equals("rewind")) {
330: if (elements[1] != null) {
331: listener.rewind(Integer.parseInt(elements[1]));
332: } else {
333: listener.rewind();
334: }
335: } else if (elements[0].equals("beginBacktrack")) {
336: listener.beginBacktrack(Integer.parseInt(elements[1]));
337: } else if (elements[0].equals("endBacktrack")) {
338: int level = Integer.parseInt(elements[1]);
339: int successI = Integer.parseInt(elements[2]);
340: listener.endBacktrack(level,
341: successI == DebugEventListener.TRUE);
342: } else if (elements[0].equals("exception")) {
343: String excName = elements[1];
344: String indexS = elements[2];
345: String lineS = elements[3];
346: String posS = elements[4];
347: Class excClass = null;
348: try {
349: excClass = Class.forName(excName);
350: RecognitionException e = (RecognitionException) excClass
351: .newInstance();
352: e.index = Integer.parseInt(indexS);
353: e.line = Integer.parseInt(lineS);
354: e.charPositionInLine = Integer.parseInt(posS);
355: listener.recognitionException(e);
356: } catch (ClassNotFoundException cnfe) {
357: System.err.println("can't find class " + cnfe);
358: cnfe.printStackTrace(System.err);
359: } catch (InstantiationException ie) {
360: System.err.println("can't instantiate class " + ie);
361: ie.printStackTrace(System.err);
362: } catch (IllegalAccessException iae) {
363: System.err.println("can't access class " + iae);
364: iae.printStackTrace(System.err);
365: }
366: } else if (elements[0].equals("beginResync")) {
367: listener.beginResync();
368: } else if (elements[0].equals("endResync")) {
369: listener.endResync();
370: } else if (elements[0].equals("terminate")) {
371: listener.terminate();
372: } else if (elements[0].equals("semanticPredicate")) {
373: Boolean result = Boolean.valueOf(elements[1]);
374: String predicateText = elements[2];
375: predicateText = unEscapeNewlines(predicateText);
376: listener.semanticPredicate(result.booleanValue(),
377: predicateText);
378: } else if (elements[0].equals("consumeNode")) {
379: ProxyTree node = deserializeNode(elements, 1);
380: listener.consumeNode(node);
381: } else if (elements[0].equals("LN")) {
382: int i = Integer.valueOf(elements[1]);
383: ProxyTree node = deserializeNode(elements, 2);
384: listener.LT(i, node);
385: } else if (elements[0].equals("createNodeFromTokenElements")) {
386: int ID = Integer.valueOf(elements[1]);
387: int type = Integer.valueOf(elements[2]);
388: String text = elements[3];
389: text = unEscapeNewlines(text);
390: ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
391: listener.createNode(node);
392: } else if (elements[0].equals("createNode")) {
393: int ID = Integer.valueOf(elements[1]);
394: int tokenIndex = Integer.valueOf(elements[2]);
395: // create dummy node/token filled with ID, tokenIndex
396: ProxyTree node = new ProxyTree(ID);
397: ProxyToken token = new ProxyToken(tokenIndex);
398: listener.createNode(node, token);
399: } else if (elements[0].equals("nilNode")) {
400: int ID = Integer.valueOf(elements[1]);
401: ProxyTree node = new ProxyTree(ID);
402: listener.nilNode(node);
403: } else if (elements[0].equals("becomeRoot")) {
404: int newRootID = Integer.valueOf(elements[1]);
405: int oldRootID = Integer.valueOf(elements[2]);
406: ProxyTree newRoot = new ProxyTree(newRootID);
407: ProxyTree oldRoot = new ProxyTree(oldRootID);
408: listener.becomeRoot(newRoot, oldRoot);
409: } else if (elements[0].equals("addChild")) {
410: int rootID = Integer.valueOf(elements[1]);
411: int childID = Integer.valueOf(elements[2]);
412: ProxyTree root = new ProxyTree(rootID);
413: ProxyTree child = new ProxyTree(childID);
414: listener.addChild(root, child);
415: } else if (elements[0].equals("setTokenBoundaries")) {
416: int ID = Integer.valueOf(elements[1]);
417: ProxyTree node = new ProxyTree(ID);
418: listener.setTokenBoundaries(node, Integer
419: .parseInt(elements[2]), Integer
420: .parseInt(elements[3]));
421: } else {
422: System.err.println("unknown debug event: " + line);
423: }
424: }
425:
426: protected ProxyTree deserializeNode(String[] elements, int offset) {
427: int ID = Integer.valueOf(elements[offset + 0]);
428: int type = Integer.valueOf(elements[offset + 1]);
429: int tokenLine = Integer.valueOf(elements[offset + 2]);
430: int charPositionInLine = Integer.valueOf(elements[offset + 3]);
431: int tokenIndex = Integer.valueOf(elements[offset + 4]);
432: String text = elements[offset + 5];
433: text = unEscapeNewlines(text);
434: return new ProxyTree(ID, type, tokenLine, charPositionInLine,
435: tokenIndex, text);
436: }
437:
438: protected ProxyToken deserializeToken(String[] elements, int offset) {
439: String indexS = elements[offset + 0];
440: String typeS = elements[offset + 1];
441: String channelS = elements[offset + 2];
442: String lineS = elements[offset + 3];
443: String posS = elements[offset + 4];
444: String text = elements[offset + 5];
445: text = unEscapeNewlines(text);
446: int index = Integer.parseInt(indexS);
447: ProxyToken t = new ProxyToken(index, Integer.parseInt(typeS),
448: Integer.parseInt(channelS), Integer.parseInt(lineS),
449: Integer.parseInt(posS), text);
450: return t;
451: }
452:
453: /** Create a thread to listen to the remote running recognizer */
454: public void start() {
455: Thread t = new Thread(this );
456: t.start();
457: }
458:
459: public void run() {
460: eventHandler();
461: }
462:
463: // M i s c
464:
465: public String[] getEventElements(String event) {
466: if (event == null) {
467: return null;
468: }
469: String[] elements = new String[MAX_EVENT_ELEMENTS];
470: String str = null; // a string element if present (must be last)
471: try {
472: int firstQuoteIndex = event.indexOf('"');
473: if (firstQuoteIndex >= 0) {
474: // treat specially; has a string argument like "a comment\n
475: // Note that the string is terminated by \n not end quote.
476: // Easier to parse that way.
477: String eventWithoutString = event.substring(0,
478: firstQuoteIndex);
479: str = event.substring(firstQuoteIndex + 1, event
480: .length());
481: event = eventWithoutString;
482: }
483: StringTokenizer st = new StringTokenizer(event, " \t",
484: false);
485: int i = 0;
486: while (st.hasMoreTokens()) {
487: if (i >= MAX_EVENT_ELEMENTS) {
488: // ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event);
489: return elements;
490: }
491: elements[i] = st.nextToken();
492: i++;
493: }
494: if (str != null) {
495: elements[i] = str;
496: }
497: } catch (Exception e) {
498: e.printStackTrace(System.err);
499: }
500: return elements;
501: }
502:
503: protected String unEscapeNewlines(String txt) {
504: // this unescape is slow but easy to understand
505: txt = txt.replaceAll("%0A", "\n"); // unescape \n
506: txt = txt.replaceAll("%0D", "\r"); // unescape \r
507: txt = txt.replaceAll("%25", "%"); // undo escaped escape chars
508: return txt;
509: }
510:
511: public boolean tokenIndexesAreInvalid() {
512: return false;
513: //return tokenIndexesInvalid;
514: }
515:
516: }
|