/*
* [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;
/**
* 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.
*
*/
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;
/** Who am i debugging? */
protected BaseRecognizer recognizer;
/**
* 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. :(
*
*/
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
}
}