blob: e390065fa7e99a2dc0a0f3ab3c74b12980445c8a [file] [log] [blame]
/*
* [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
}
}