| /* |
| * [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 System; |
| using Antlr.Runtime.JavaExtensions; |
| |
| using IOException = System.IO.IOException; |
| using ITreeAdaptor = Antlr.Runtime.Tree.ITreeAdaptor; |
| using Socket = System.Net.Sockets.Socket; |
| using StringBuilder = System.Text.StringBuilder; |
| using TcpListener = System.Net.Sockets.TcpListener; |
| |
| /** <summary> |
| * A proxy debug event listener that forwards events over a socket to |
| * a debugger (or any other listener) using a simple text-based protocol; |
| * one event per line. ANTLRWorks listens on server socket with a |
| * RemoteDebugEventSocketListener instance. These two objects must therefore |
| * be kept in sync. New events must be handled on both sides of socket. |
| * </summary> |
| */ |
| public class DebugEventSocketProxy : BlankDebugEventListener |
| { |
| public const int DefaultDebuggerPort = 49100; |
| protected int port = DefaultDebuggerPort; |
| protected TcpListener serverSocket; |
| protected Socket socket; |
| protected string grammarFileName; |
| //protected PrintWriter @out; |
| //protected BufferedReader @in; |
| |
| /** <summary>Who am i debugging?</summary> */ |
| protected BaseRecognizer recognizer; |
| |
| /** <summary> |
| * Almost certainly the recognizer will have adaptor set, but |
| * we don't know how to cast it (Parser or TreeParser) to get |
| * the adaptor field. Must be set with a constructor. :( |
| * </summary> |
| */ |
| protected ITreeAdaptor adaptor; |
| |
| public DebugEventSocketProxy( BaseRecognizer recognizer, ITreeAdaptor adaptor ) : |
| this( recognizer, DefaultDebuggerPort, adaptor ) |
| { |
| } |
| |
| public DebugEventSocketProxy( BaseRecognizer recognizer, int port, ITreeAdaptor adaptor ) |
| { |
| this.grammarFileName = recognizer.GrammarFileName; |
| this.adaptor = adaptor; |
| this.port = port; |
| } |
| |
| #region Properties |
| public virtual ITreeAdaptor TreeAdaptor |
| { |
| get |
| { |
| return adaptor; |
| } |
| set |
| { |
| adaptor = value; |
| } |
| } |
| #endregion |
| |
| public virtual void Handshake() |
| { |
| if ( serverSocket == null ) |
| { |
| System.Net.IPHostEntry hostInfo = System.Net.Dns.GetHostEntry( "localhost" ); |
| System.Net.IPAddress ipAddress = hostInfo.AddressList[0]; |
| serverSocket = new TcpListener( ipAddress, port ); |
| socket = serverSocket.AcceptSocket(); |
| socket.NoDelay = true; |
| |
| System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); |
| socket.Send( encoding.GetBytes( "ANTLR " + DebugEventListenerConstants.ProtocolVersion + "\n" ) ); |
| socket.Send( encoding.GetBytes( "grammar \"" + grammarFileName + "\n" ) ); |
| Ack(); |
| |
| //serverSocket = new ServerSocket( port ); |
| //socket = serverSocket.accept(); |
| //socket.setTcpNoDelay( true ); |
| //OutputStream os = socket.getOutputStream(); |
| //OutputStreamWriter osw = new OutputStreamWriter( os, "UTF8" ); |
| //@out = new PrintWriter( new BufferedWriter( osw ) ); |
| //InputStream @is = socket.getInputStream(); |
| //InputStreamReader isr = new InputStreamReader( @is, "UTF8" ); |
| //@in = new BufferedReader( isr ); |
| //@out.println( "ANTLR " + DebugEventListenerConstants.PROTOCOL_VERSION ); |
| //@out.println( "grammar \"" + grammarFileName ); |
| //@out.flush(); |
| //ack(); |
| } |
| } |
| |
| public override void Commence() |
| { |
| // don't bother sending event; listener will trigger upon connection |
| } |
| |
| public override void Terminate() |
| { |
| Transmit( "terminate" ); |
| //@out.close(); |
| try |
| { |
| socket.Close(); |
| } |
| catch ( IOException ioe ) |
| { |
| ExceptionExtensions.PrintStackTrace( ioe, Console.Error ); |
| } |
| } |
| |
| protected virtual void Ack() |
| { |
| try |
| { |
| throw new NotImplementedException(); |
| //@in.readLine(); |
| } |
| catch ( IOException ioe ) |
| { |
| ExceptionExtensions.PrintStackTrace( ioe, Console.Error ); |
| } |
| } |
| |
| protected virtual void Transmit( string @event ) |
| { |
| socket.Send( new System.Text.UTF8Encoding().GetBytes( @event + "\n" ) ); |
| //@out.println( @event ); |
| //@out.flush(); |
| Ack(); |
| } |
| |
| public override void EnterRule( string grammarFileName, string ruleName ) |
| { |
| Transmit( "enterRule\t" + grammarFileName + "\t" + ruleName ); |
| } |
| |
| public override void EnterAlt( int alt ) |
| { |
| Transmit( "enterAlt\t" + alt ); |
| } |
| |
| public override void ExitRule( string grammarFileName, string ruleName ) |
| { |
| Transmit( "exitRule\t" + grammarFileName + "\t" + ruleName ); |
| } |
| |
| public override void EnterSubRule( int decisionNumber ) |
| { |
| Transmit( "enterSubRule\t" + decisionNumber ); |
| } |
| |
| public override void ExitSubRule( int decisionNumber ) |
| { |
| Transmit( "exitSubRule\t" + decisionNumber ); |
| } |
| |
| public override void EnterDecision(int decisionNumber, bool couldBacktrack) |
| { |
| Transmit( "enterDecision\t" + decisionNumber ); |
| } |
| |
| public override void ExitDecision( int decisionNumber ) |
| { |
| Transmit( "exitDecision\t" + decisionNumber ); |
| } |
| |
| public override void ConsumeToken( IToken t ) |
| { |
| string buf = SerializeToken( t ); |
| Transmit( "consumeToken\t" + buf ); |
| } |
| |
| public override void ConsumeHiddenToken( IToken t ) |
| { |
| string buf = SerializeToken( t ); |
| Transmit( "consumeHiddenToken\t" + buf ); |
| } |
| |
| public override void LT( int i, IToken t ) |
| { |
| if ( t != null ) |
| Transmit( "LT\t" + i + "\t" + SerializeToken( t ) ); |
| } |
| |
| public override void Mark( int i ) |
| { |
| Transmit( "mark\t" + i ); |
| } |
| |
| public override void Rewind( int i ) |
| { |
| Transmit( "rewind\t" + i ); |
| } |
| |
| public override void Rewind() |
| { |
| Transmit( "rewind" ); |
| } |
| |
| public override void BeginBacktrack( int level ) |
| { |
| Transmit( "beginBacktrack\t" + level ); |
| } |
| |
| public override void EndBacktrack( int level, bool successful ) |
| { |
| Transmit( "endBacktrack\t" + level + "\t" + ( successful ? DebugEventListenerConstants.True : DebugEventListenerConstants.False ) ); |
| } |
| |
| public override void Location( int line, int pos ) |
| { |
| Transmit( "location\t" + line + "\t" + pos ); |
| } |
| |
| public override void RecognitionException( RecognitionException e ) |
| { |
| StringBuilder buf = new StringBuilder( 50 ); |
| buf.Append( "exception\t" ); |
| buf.Append( e.GetType().Name ); |
| // dump only the data common to all exceptions for now |
| buf.Append( "\t" ); |
| buf.Append( e.Index ); |
| buf.Append( "\t" ); |
| buf.Append( e.Line ); |
| buf.Append( "\t" ); |
| buf.Append( e.CharPositionInLine ); |
| Transmit( buf.ToString() ); |
| } |
| |
| public override void BeginResync() |
| { |
| Transmit( "beginResync" ); |
| } |
| |
| public override void EndResync() |
| { |
| Transmit( "endResync" ); |
| } |
| |
| public override void SemanticPredicate( bool result, string predicate ) |
| { |
| StringBuilder buf = new StringBuilder( 50 ); |
| buf.Append( "semanticPredicate\t" ); |
| buf.Append( result ); |
| SerializeText( buf, predicate ); |
| Transmit( buf.ToString() ); |
| } |
| |
| #region AST Parsing Events |
| |
| public override void ConsumeNode( object t ) |
| { |
| StringBuilder buf = new StringBuilder( 50 ); |
| buf.Append( "consumeNode" ); |
| SerializeNode( buf, t ); |
| Transmit( buf.ToString() ); |
| } |
| |
| public override void LT( int i, object t ) |
| { |
| int ID = adaptor.GetUniqueID( t ); |
| string text = adaptor.GetText( t ); |
| int type = adaptor.GetType( t ); |
| StringBuilder buf = new StringBuilder( 50 ); |
| buf.Append( "LN\t" ); // lookahead node; distinguish from LT in protocol |
| buf.Append( i ); |
| SerializeNode( buf, t ); |
| Transmit( buf.ToString() ); |
| } |
| |
| protected virtual void SerializeNode( StringBuilder buf, object t ) |
| { |
| int ID = adaptor.GetUniqueID( t ); |
| string text = adaptor.GetText( t ); |
| int type = adaptor.GetType( t ); |
| buf.Append( "\t" ); |
| buf.Append( ID ); |
| buf.Append( "\t" ); |
| buf.Append( type ); |
| IToken token = adaptor.GetToken( t ); |
| int line = -1; |
| int pos = -1; |
| if ( token != null ) |
| { |
| line = token.Line; |
| pos = token.CharPositionInLine; |
| } |
| buf.Append( "\t" ); |
| buf.Append( line ); |
| buf.Append( "\t" ); |
| buf.Append( pos ); |
| int tokenIndex = adaptor.GetTokenStartIndex( t ); |
| buf.Append( "\t" ); |
| buf.Append( tokenIndex ); |
| SerializeText( buf, text ); |
| } |
| |
| #endregion |
| |
| |
| #region AST Events |
| |
| public override void NilNode( object t ) |
| { |
| int ID = adaptor.GetUniqueID( t ); |
| Transmit( "nilNode\t" + ID ); |
| } |
| |
| public override void ErrorNode( object t ) |
| { |
| int ID = adaptor.GetUniqueID( t ); |
| string text = t.ToString(); |
| StringBuilder buf = new StringBuilder( 50 ); |
| buf.Append( "errorNode\t" ); |
| buf.Append( ID ); |
| buf.Append( "\t" ); |
| buf.Append( TokenTypes.Invalid ); |
| SerializeText( buf, text ); |
| Transmit( buf.ToString() ); |
| } |
| |
| public override void CreateNode( object t ) |
| { |
| int ID = adaptor.GetUniqueID( t ); |
| string text = adaptor.GetText( t ); |
| int type = adaptor.GetType( t ); |
| StringBuilder buf = new StringBuilder( 50 ); |
| buf.Append( "createNodeFromTokenElements\t" ); |
| buf.Append( ID ); |
| buf.Append( "\t" ); |
| buf.Append( type ); |
| SerializeText( buf, text ); |
| Transmit( buf.ToString() ); |
| } |
| |
| public override void CreateNode( object node, IToken token ) |
| { |
| int ID = adaptor.GetUniqueID( node ); |
| int tokenIndex = token.TokenIndex; |
| Transmit( "createNode\t" + ID + "\t" + tokenIndex ); |
| } |
| |
| public override void BecomeRoot( object newRoot, object oldRoot ) |
| { |
| int newRootID = adaptor.GetUniqueID( newRoot ); |
| int oldRootID = adaptor.GetUniqueID( oldRoot ); |
| Transmit( "becomeRoot\t" + newRootID + "\t" + oldRootID ); |
| } |
| |
| public override void AddChild( object root, object child ) |
| { |
| int rootID = adaptor.GetUniqueID( root ); |
| int childID = adaptor.GetUniqueID( child ); |
| Transmit( "addChild\t" + rootID + "\t" + childID ); |
| } |
| |
| public override void SetTokenBoundaries( object t, int tokenStartIndex, int tokenStopIndex ) |
| { |
| int ID = adaptor.GetUniqueID( t ); |
| Transmit( "setTokenBoundaries\t" + ID + "\t" + tokenStartIndex + "\t" + tokenStopIndex ); |
| } |
| |
| #endregion |
| |
| |
| #region Support |
| |
| protected virtual string SerializeToken( IToken t ) |
| { |
| StringBuilder buf = new StringBuilder( 50 ); |
| buf.Append( t.TokenIndex ); |
| buf.Append( '\t' ); |
| buf.Append( t.Type ); |
| buf.Append( '\t' ); |
| buf.Append( t.Channel ); |
| buf.Append( '\t' ); |
| buf.Append( t.Line ); |
| buf.Append( '\t' ); |
| buf.Append( t.CharPositionInLine ); |
| SerializeText( buf, t.Text ); |
| return buf.ToString(); |
| } |
| |
| protected virtual void SerializeText( StringBuilder buf, string text ) |
| { |
| buf.Append( "\t\"" ); |
| if ( text == null ) |
| { |
| text = ""; |
| } |
| // escape \n and \r all text for token appears to exist on one line |
| // this escape is slow but easy to understand |
| text = EscapeNewlines( text ); |
| buf.Append( text ); |
| } |
| |
| protected virtual string EscapeNewlines( string txt ) |
| { |
| txt = txt.Replace( "%", "%25" ); // escape all escape char ;) |
| txt = txt.Replace( "\n", "%0A" ); // escape \n |
| txt = txt.Replace( "\r", "%0D" ); // escape \r |
| return txt; |
| } |
| |
| #endregion |
| } |
| } |