| /* |
| * Note to JL: Refactored extension methods |
| * |
| * [The "BSD licence"] |
| * Copyright (c) 2005-2008 Terence Parr |
| * All rights reserved. |
| * |
| * Conversion to C#: |
| * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| namespace Antlr.Runtime.Debug { |
| using Antlr.Runtime.JavaExtensions; |
| using BaseTree = Antlr.Runtime.Tree.BaseTree; |
| using Console = System.Console; |
| using Exception = System.Exception; |
| using IOException = System.IO.IOException; |
| using ITree = Antlr.Runtime.Tree.ITree; |
| using Socket = System.Net.Sockets.Socket; |
| using SocketException = System.Net.Sockets.SocketException; |
| using TextReader = System.IO.TextReader; |
| using TextWriter = System.IO.TextWriter; |
| |
| public class RemoteDebugEventSocketListener { |
| const int MAX_EVENT_ELEMENTS = 8; |
| IDebugEventListener listener; |
| string machine; |
| int port; |
| Socket channel = null; |
| TextWriter @out; |
| TextReader @in; |
| string @event; |
| /** <summary>Version of ANTLR (dictates events)</summary> */ |
| public string version; |
| public string grammarFileName; |
| /** <summary> |
| * Track the last token index we saw during a consume. If same, then |
| * set a flag that we have a problem. |
| * </summary> |
| */ |
| int previousTokenIndex = -1; |
| bool tokenIndexesInvalid = false; |
| |
| public class ProxyToken : IToken { |
| int index; |
| int type; |
| int channel; |
| int line; |
| int charPos; |
| string text; |
| public ProxyToken(int index) { |
| this.index = index; |
| } |
| public ProxyToken(int index, int type, int channel, |
| int line, int charPos, string text) { |
| this.index = index; |
| this.type = type; |
| this.channel = channel; |
| this.line = line; |
| this.charPos = charPos; |
| this.text = text; |
| } |
| |
| #region IToken Members |
| public string Text { |
| get { |
| return text; |
| } |
| set { |
| text = value; |
| } |
| } |
| |
| public int Type { |
| get { |
| return type; |
| } |
| set { |
| type = value; |
| } |
| } |
| |
| public int Line { |
| get { |
| return line; |
| } |
| set { |
| line = value; |
| } |
| } |
| |
| public int CharPositionInLine { |
| get { |
| return charPos; |
| } |
| set { |
| charPos = value; |
| } |
| } |
| |
| public int Channel { |
| get { |
| return channel; |
| } |
| set { |
| channel = value; |
| } |
| } |
| |
| public int StartIndex { |
| get { |
| return -1; |
| } |
| set { |
| } |
| } |
| |
| public int StopIndex { |
| get { |
| return -1; |
| } |
| set { |
| } |
| } |
| |
| public int TokenIndex { |
| get { |
| return index; |
| } |
| set { |
| index = value; |
| } |
| } |
| |
| public ICharStream InputStream { |
| get { |
| return null; |
| } |
| set { |
| } |
| } |
| |
| #endregion |
| |
| public override string ToString() { |
| string channelStr = ""; |
| if (channel != TokenChannels.Default) { |
| channelStr = ",channel=" + channel; |
| } |
| return "[" + Text + "/<" + type + ">" + channelStr + "," + line + ":" + CharPositionInLine + ",@" + index + "]"; |
| } |
| } |
| |
| public class ProxyTree : BaseTree { |
| public int ID; |
| int type; |
| int line = 0; |
| public int charPos = -1; |
| public int tokenIndex = -1; |
| string text; |
| |
| public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, string text) { |
| this.ID = ID; |
| this.type = type; |
| this.line = line; |
| this.charPos = charPos; |
| this.tokenIndex = tokenIndex; |
| this.text = text; |
| } |
| |
| public ProxyTree(int ID) { |
| this.ID = ID; |
| } |
| |
| #region Properties |
| public override string Text { |
| get { |
| return text; |
| } |
| set { |
| } |
| } |
| public override int TokenStartIndex { |
| get { |
| return tokenIndex; |
| } |
| set { |
| } |
| } |
| public override int TokenStopIndex { |
| get { |
| return 0; |
| } |
| set { |
| } |
| } |
| public override int Type { |
| get { |
| return type; |
| } |
| set { |
| } |
| } |
| #endregion |
| |
| public override ITree DupNode() { |
| return null; |
| } |
| |
| public override string ToString() { |
| return "fix this"; |
| } |
| } |
| |
| public RemoteDebugEventSocketListener(IDebugEventListener listener, |
| string machine, |
| int port) { |
| this.listener = listener; |
| this.machine = machine; |
| this.port = port; |
| |
| if (!OpenConnection()) { |
| throw new SocketException(); |
| } |
| } |
| |
| protected virtual void EventHandler() { |
| try { |
| Handshake(); |
| @event = @in.ReadLine(); |
| while (@event != null) { |
| Dispatch(@event); |
| Ack(); |
| @event = @in.ReadLine(); |
| } |
| } catch (Exception e) { |
| Console.Error.WriteLine(e); |
| ExceptionExtensions.PrintStackTrace(e, Console.Error); |
| } finally { |
| CloseConnection(); |
| } |
| } |
| |
| protected virtual bool OpenConnection() { |
| bool success = false; |
| try { |
| throw new System.NotImplementedException(); |
| //channel = new Socket( machine, port ); |
| //channel.setTcpNoDelay( true ); |
| //OutputStream os = channel.getOutputStream(); |
| //OutputStreamWriter osw = new OutputStreamWriter( os, "UTF8" ); |
| //@out = new PrintWriter( new BufferedWriter( osw ) ); |
| //InputStream @is = channel.getInputStream(); |
| //InputStreamReader isr = new InputStreamReader( @is, "UTF8" ); |
| //@in = new BufferedReader( isr ); |
| //success = true; |
| } catch (Exception e) { |
| Console.Error.WriteLine(e); |
| } |
| return success; |
| } |
| |
| protected virtual void CloseConnection() { |
| try { |
| @in.Close(); |
| @in = null; |
| @out.Close(); |
| @out = null; |
| channel.Close(); |
| channel = null; |
| } catch (Exception e) { |
| Console.Error.WriteLine(e); |
| ExceptionExtensions.PrintStackTrace(e, Console.Error); |
| } finally { |
| if (@in != null) { |
| try { |
| @in.Close(); |
| } catch (IOException ioe) { |
| Console.Error.WriteLine(ioe); |
| } |
| } |
| if (@out != null) { |
| @out.Close(); |
| } |
| if (channel != null) { |
| try { |
| channel.Close(); |
| } catch (IOException ioe) { |
| Console.Error.WriteLine(ioe); |
| } |
| } |
| } |
| |
| } |
| |
| protected virtual void Handshake() { |
| string antlrLine = @in.ReadLine(); |
| string[] antlrElements = GetEventElements(antlrLine); |
| version = antlrElements[1]; |
| string grammarLine = @in.ReadLine(); |
| string[] grammarElements = GetEventElements(grammarLine); |
| grammarFileName = grammarElements[1]; |
| Ack(); |
| listener.Commence(); // inform listener after handshake |
| } |
| |
| protected virtual void Ack() { |
| @out.WriteLine("ack"); |
| @out.Flush(); |
| } |
| |
| protected virtual void Dispatch(string line) { |
| //JSystem.@out.println( "event: " + line ); |
| string[] elements = GetEventElements(line); |
| if (elements == null || elements[0] == null) { |
| Console.Error.WriteLine("unknown debug event: " + line); |
| return; |
| } |
| if (elements[0].Equals("enterRule")) { |
| listener.EnterRule(elements[1], elements[2]); |
| } else if (elements[0].Equals("exitRule")) { |
| listener.ExitRule(elements[1], elements[2]); |
| } else if (elements[0].Equals("enterAlt")) { |
| listener.EnterAlt(int.Parse(elements[1])); |
| } else if (elements[0].Equals("enterSubRule")) { |
| listener.EnterSubRule(int.Parse(elements[1])); |
| } else if (elements[0].Equals("exitSubRule")) { |
| listener.ExitSubRule(int.Parse(elements[1])); |
| } else if (elements[0].Equals("enterDecision")) { |
| listener.EnterDecision(int.Parse(elements[1]), elements[2].Equals("true")); |
| } else if (elements[0].Equals("exitDecision")) { |
| listener.ExitDecision(int.Parse(elements[1])); |
| } else if (elements[0].Equals("location")) { |
| listener.Location(int.Parse(elements[1]), |
| int.Parse(elements[2])); |
| } else if (elements[0].Equals("consumeToken")) { |
| ProxyToken t = DeserializeToken(elements, 1); |
| if (t.TokenIndex == previousTokenIndex) { |
| tokenIndexesInvalid = true; |
| } |
| previousTokenIndex = t.TokenIndex; |
| listener.ConsumeToken(t); |
| } else if (elements[0].Equals("consumeHiddenToken")) { |
| ProxyToken t = DeserializeToken(elements, 1); |
| if (t.TokenIndex == previousTokenIndex) { |
| tokenIndexesInvalid = true; |
| } |
| previousTokenIndex = t.TokenIndex; |
| listener.ConsumeHiddenToken(t); |
| } else if (elements[0].Equals("LT")) { |
| IToken t = DeserializeToken(elements, 2); |
| listener.LT(int.Parse(elements[1]), t); |
| } else if (elements[0].Equals("mark")) { |
| listener.Mark(int.Parse(elements[1])); |
| } else if (elements[0].Equals("rewind")) { |
| if (elements[1] != null) { |
| listener.Rewind(int.Parse(elements[1])); |
| } else { |
| listener.Rewind(); |
| } |
| } else if (elements[0].Equals("beginBacktrack")) { |
| listener.BeginBacktrack(int.Parse(elements[1])); |
| } else if (elements[0].Equals("endBacktrack")) { |
| int level = int.Parse(elements[1]); |
| int successI = int.Parse(elements[2]); |
| listener.EndBacktrack(level, successI == DebugEventListenerConstants.True); |
| } else if (elements[0].Equals("exception")) { |
| #if true |
| throw new System.NotImplementedException(); |
| #else |
| string excName = elements[1]; |
| string indexS = elements[2]; |
| string lineS = elements[3]; |
| string posS = elements[4]; |
| Class excClass = null; |
| try |
| { |
| excClass = Class.forName( excName ); |
| RecognitionException e = |
| (RecognitionException)excClass.newInstance(); |
| e.index = int.Parse( indexS ); |
| e.line = int.Parse( lineS ); |
| e.charPositionInLine = int.Parse( posS ); |
| listener.recognitionException( e ); |
| } |
| catch ( ClassNotFoundException cnfe ) |
| { |
| Console.Error.println( "can't find class " + cnfe ); |
| cnfe.printStackTrace( Console.Error ); |
| } |
| catch ( InstantiationException ie ) |
| { |
| Console.Error.println( "can't instantiate class " + ie ); |
| ie.printStackTrace( Console.Error ); |
| } |
| catch ( IllegalAccessException iae ) |
| { |
| Console.Error.println( "can't access class " + iae ); |
| iae.printStackTrace( Console.Error ); |
| } |
| #endif |
| } else if (elements[0].Equals("beginResync")) { |
| listener.BeginResync(); |
| } else if (elements[0].Equals("endResync")) { |
| listener.EndResync(); |
| } else if (elements[0].Equals("terminate")) { |
| listener.Terminate(); |
| } else if (elements[0].Equals("semanticPredicate")) { |
| bool result = bool.Parse(elements[1]); |
| string predicateText = elements[2]; |
| predicateText = UnEscapeNewlines(predicateText); |
| listener.SemanticPredicate(result, |
| predicateText); |
| } else if (elements[0].Equals("consumeNode")) { |
| ProxyTree node = DeserializeNode(elements, 1); |
| listener.ConsumeNode(node); |
| } else if (elements[0].Equals("LN")) { |
| int i = int.Parse(elements[1]); |
| ProxyTree node = DeserializeNode(elements, 2); |
| listener.LT(i, node); |
| } else if (elements[0].Equals("createNodeFromTokenElements")) { |
| int ID = int.Parse(elements[1]); |
| int type = int.Parse(elements[2]); |
| string text = elements[3]; |
| text = UnEscapeNewlines(text); |
| ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); |
| listener.CreateNode(node); |
| } else if (elements[0].Equals("createNode")) { |
| int ID = int.Parse(elements[1]); |
| int tokenIndex = int.Parse(elements[2]); |
| // create dummy node/token filled with ID, tokenIndex |
| ProxyTree node = new ProxyTree(ID); |
| ProxyToken token = new ProxyToken(tokenIndex); |
| listener.CreateNode(node, token); |
| } else if (elements[0].Equals("nilNode")) { |
| int ID = int.Parse(elements[1]); |
| ProxyTree node = new ProxyTree(ID); |
| listener.NilNode(node); |
| } else if (elements[0].Equals("errorNode")) { |
| // TODO: do we need a special tree here? |
| int ID = int.Parse(elements[1]); |
| int type = int.Parse(elements[2]); |
| string text = elements[3]; |
| text = UnEscapeNewlines(text); |
| ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); |
| listener.ErrorNode(node); |
| } else if (elements[0].Equals("becomeRoot")) { |
| int newRootID = int.Parse(elements[1]); |
| int oldRootID = int.Parse(elements[2]); |
| ProxyTree newRoot = new ProxyTree(newRootID); |
| ProxyTree oldRoot = new ProxyTree(oldRootID); |
| listener.BecomeRoot(newRoot, oldRoot); |
| } else if (elements[0].Equals("addChild")) { |
| int rootID = int.Parse(elements[1]); |
| int childID = int.Parse(elements[2]); |
| ProxyTree root = new ProxyTree(rootID); |
| ProxyTree child = new ProxyTree(childID); |
| listener.AddChild(root, child); |
| } else if (elements[0].Equals("setTokenBoundaries")) { |
| int ID = int.Parse(elements[1]); |
| ProxyTree node = new ProxyTree(ID); |
| listener.SetTokenBoundaries( |
| node, |
| int.Parse(elements[2]), |
| int.Parse(elements[3])); |
| } else { |
| Console.Error.WriteLine("unknown debug event: " + line); |
| } |
| } |
| |
| protected virtual ProxyTree DeserializeNode(string[] elements, int offset) { |
| int ID = int.Parse(elements[offset + 0]); |
| int type = int.Parse(elements[offset + 1]); |
| int tokenLine = int.Parse(elements[offset + 2]); |
| int charPositionInLine = int.Parse(elements[offset + 3]); |
| int tokenIndex = int.Parse(elements[offset + 4]); |
| string text = elements[offset + 5]; |
| text = UnEscapeNewlines(text); |
| return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text); |
| } |
| |
| protected virtual ProxyToken DeserializeToken(string[] elements, |
| int offset) { |
| string indexS = elements[offset + 0]; |
| string typeS = elements[offset + 1]; |
| string channelS = elements[offset + 2]; |
| string lineS = elements[offset + 3]; |
| string posS = elements[offset + 4]; |
| string text = elements[offset + 5]; |
| text = UnEscapeNewlines(text); |
| int index = int.Parse(indexS); |
| ProxyToken t = |
| new ProxyToken(index, |
| int.Parse(typeS), |
| int.Parse(channelS), |
| int.Parse(lineS), |
| int.Parse(posS), |
| text); |
| return t; |
| } |
| |
| /** <summary>Create a thread to listen to the remote running recognizer</summary> */ |
| public virtual void Start() { |
| System.Threading.Thread t = new System.Threading.Thread(Run); |
| t.Start(); |
| } |
| |
| public virtual void Run() { |
| EventHandler(); |
| } |
| |
| #region Misc |
| |
| public virtual string[] GetEventElements(string @event) { |
| if (@event == null) { |
| return null; |
| } |
| string[] elements = new string[MAX_EVENT_ELEMENTS]; |
| string str = null; // a string element if present (must be last) |
| try { |
| int firstQuoteIndex = @event.IndexOf('"'); |
| if (firstQuoteIndex >= 0) { |
| // treat specially; has a string argument like "a comment\n |
| // Note that the string is terminated by \n not end quote. |
| // Easier to parse that way. |
| string eventWithoutString = @event.Substring(0, firstQuoteIndex); |
| str = @event.Substring(firstQuoteIndex + 1); |
| @event = eventWithoutString; |
| } |
| StringTokenizer st = new StringTokenizer(@event, "\t", false); |
| int i = 0; |
| while (st.hasMoreTokens()) { |
| if (i >= MAX_EVENT_ELEMENTS) { |
| // ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event); |
| return elements; |
| } |
| elements[i] = st.nextToken(); |
| i++; |
| } |
| if (str != null) { |
| elements[i] = str; |
| } |
| } catch (Exception e) { |
| ExceptionExtensions.PrintStackTrace(e, Console.Error); |
| } |
| return elements; |
| } |
| |
| protected virtual string UnEscapeNewlines(string txt) { |
| // this unescape is slow but easy to understand |
| txt = StringExtensions.replaceAll(txt, "%0A", "\n"); // unescape \n |
| txt = StringExtensions.replaceAll(txt, "%0D", "\r"); // unescape \r |
| txt = StringExtensions.replaceAll(txt, "%25", "%"); // undo escaped escape chars |
| return txt; |
| } |
| |
| public virtual bool TokenIndexesAreInvalid() { |
| return false; |
| //return tokenIndexesInvalid; |
| } |
| |
| #endregion |
| |
| } |
| } |