| /* |
| * [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 Array = System.Array; |
| 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 Math = System.Math; |
| 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; |
| } |
| |
| string[] tokens = @event.Split('\t'); |
| Array.Copy(tokens, elements, Math.Min(tokens.Length, MAX_EVENT_ELEMENTS)); |
| if (tokens.Length >= MAX_EVENT_ELEMENTS) |
| return elements; |
| |
| if ( str != null ) |
| { |
| elements[tokens.Length] = 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 = txt.Replace( "%0A", "\n" ); // unescape \n |
| txt = txt.Replace( "%0D", "\r" ); // unescape \r |
| txt = txt.Replace( "%25", "%" ); // undo escaped escape chars |
| return txt; |
| } |
| |
| public virtual bool TokenIndexesAreInvalid() |
| { |
| return tokenIndexesInvalid; |
| } |
| |
| #endregion |
| |
| } |
| } |