664 lines
19 KiB
C#
664 lines
19 KiB
C#
/*
|
|
* [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.Tree
|
|
{
|
|
using System.Collections.Generic;
|
|
|
|
using Console = System.Console;
|
|
using IList = System.Collections.IList;
|
|
using InvalidOperationException = System.InvalidOperationException;
|
|
using StringBuilder = System.Text.StringBuilder;
|
|
|
|
/** <summary>A buffered stream of tree nodes. Nodes can be from a tree of ANY kind.</summary>
|
|
*
|
|
* This node stream sucks all nodes out of the tree specified in
|
|
* the constructor during construction and makes pointers into
|
|
* the tree using an array of Object pointers. The stream necessarily
|
|
* includes pointers to DOWN and UP and EOF nodes.
|
|
*
|
|
* This stream knows how to mark/release for backtracking.
|
|
*
|
|
* This stream is most suitable for tree interpreters that need to
|
|
* jump around a lot or for tree parsers requiring speed (at cost of memory).
|
|
* There is some duplicated functionality here with UnBufferedTreeNodeStream
|
|
* but just in bookkeeping, not tree walking etc...
|
|
*
|
|
* TARGET DEVELOPERS:
|
|
*
|
|
* This is the old CommonTreeNodeStream that buffered up entire node stream.
|
|
* No need to implement really as new CommonTreeNodeStream is much better
|
|
* and covers what we need.
|
|
*
|
|
* @see CommonTreeNodeStream
|
|
*/
|
|
public class BufferedTreeNodeStream : ITreeNodeStream, ITokenStreamInformation
|
|
{
|
|
public const int DEFAULT_INITIAL_BUFFER_SIZE = 100;
|
|
public const int INITIAL_CALL_STACK_SIZE = 10;
|
|
|
|
protected sealed class StreamIterator : IEnumerator<object>
|
|
{
|
|
BufferedTreeNodeStream _outer;
|
|
int _index;
|
|
|
|
public StreamIterator( BufferedTreeNodeStream outer )
|
|
{
|
|
_outer = outer;
|
|
_index = -1;
|
|
}
|
|
|
|
#region IEnumerator<object> Members
|
|
|
|
public object Current
|
|
{
|
|
get
|
|
{
|
|
if ( _index < _outer.nodes.Count )
|
|
return _outer.nodes[_index];
|
|
|
|
return _outer.eof;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable Members
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerator Members
|
|
|
|
public bool MoveNext()
|
|
{
|
|
if ( _index < _outer.nodes.Count )
|
|
_index++;
|
|
|
|
return _index < _outer.nodes.Count;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
_index = -1;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
// all these navigation nodes are shared and hence they
|
|
// cannot contain any line/column info
|
|
|
|
protected object down;
|
|
protected object up;
|
|
protected object eof;
|
|
|
|
/** <summary>The complete mapping from stream index to tree node.
|
|
* This buffer includes pointers to DOWN, UP, and EOF nodes.
|
|
* It is built upon ctor invocation. The elements are type
|
|
* Object as we don't what the trees look like.</summary>
|
|
*
|
|
* Load upon first need of the buffer so we can set token types
|
|
* of interest for reverseIndexing. Slows us down a wee bit to
|
|
* do all of the if p==-1 testing everywhere though.
|
|
*/
|
|
protected IList nodes;
|
|
|
|
/** <summary>Pull nodes from which tree?</summary> */
|
|
protected object root;
|
|
|
|
/** <summary>IF this tree (root) was created from a token stream, track it.</summary> */
|
|
protected ITokenStream tokens;
|
|
|
|
/** <summary>What tree adaptor was used to build these trees</summary> */
|
|
ITreeAdaptor adaptor;
|
|
|
|
/** <summary>Reuse same DOWN, UP navigation nodes unless this is true</summary> */
|
|
bool uniqueNavigationNodes = false;
|
|
|
|
/** <summary>The index into the nodes list of the current node (next node
|
|
* to consume). If -1, nodes array not filled yet.</summary>
|
|
*/
|
|
protected int p = -1;
|
|
|
|
/** <summary>Track the last mark() call result value for use in rewind().</summary> */
|
|
protected int lastMarker;
|
|
|
|
/** <summary>Stack of indexes used for push/pop calls</summary> */
|
|
protected Stack<int> calls;
|
|
|
|
public BufferedTreeNodeStream( object tree )
|
|
: this( new CommonTreeAdaptor(), tree )
|
|
{
|
|
}
|
|
|
|
public BufferedTreeNodeStream( ITreeAdaptor adaptor, object tree )
|
|
: this( adaptor, tree, DEFAULT_INITIAL_BUFFER_SIZE )
|
|
{
|
|
}
|
|
|
|
public BufferedTreeNodeStream( ITreeAdaptor adaptor, object tree, int initialBufferSize )
|
|
{
|
|
this.root = tree;
|
|
this.adaptor = adaptor;
|
|
nodes = new List<object>( initialBufferSize );
|
|
down = adaptor.Create( TokenTypes.Down, "DOWN" );
|
|
up = adaptor.Create( TokenTypes.Up, "UP" );
|
|
eof = adaptor.Create( TokenTypes.EndOfFile, "EOF" );
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public virtual int Count
|
|
{
|
|
get
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
throw new InvalidOperationException( "Cannot determine the Count before the buffer is filled." );
|
|
}
|
|
return nodes.Count;
|
|
}
|
|
}
|
|
|
|
public virtual object TreeSource
|
|
{
|
|
get
|
|
{
|
|
return root;
|
|
}
|
|
}
|
|
|
|
public virtual string SourceName
|
|
{
|
|
get
|
|
{
|
|
return TokenStream.SourceName;
|
|
}
|
|
}
|
|
|
|
public virtual ITokenStream TokenStream
|
|
{
|
|
get
|
|
{
|
|
return tokens;
|
|
}
|
|
set
|
|
{
|
|
tokens = value;
|
|
}
|
|
}
|
|
|
|
public virtual ITreeAdaptor TreeAdaptor
|
|
{
|
|
get
|
|
{
|
|
return adaptor;
|
|
}
|
|
set
|
|
{
|
|
adaptor = value;
|
|
}
|
|
}
|
|
|
|
public virtual bool UniqueNavigationNodes
|
|
{
|
|
get
|
|
{
|
|
return uniqueNavigationNodes;
|
|
}
|
|
set
|
|
{
|
|
uniqueNavigationNodes = value;
|
|
}
|
|
}
|
|
|
|
public virtual IToken LastToken
|
|
{
|
|
get
|
|
{
|
|
return TreeAdaptor.GetToken(LB(1));
|
|
}
|
|
}
|
|
|
|
public virtual IToken LastRealToken
|
|
{
|
|
get
|
|
{
|
|
int i = 0;
|
|
IToken token;
|
|
do
|
|
{
|
|
i++;
|
|
token = TreeAdaptor.GetToken(LB(i));
|
|
} while (token != null && token.Line <= 0);
|
|
|
|
return token;
|
|
}
|
|
}
|
|
|
|
public virtual int MaxLookBehind
|
|
{
|
|
get
|
|
{
|
|
return int.MaxValue;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/** Walk tree with depth-first-search and fill nodes buffer.
|
|
* Don't do DOWN, UP nodes if its a list (t is isNil).
|
|
*/
|
|
protected virtual void FillBuffer()
|
|
{
|
|
FillBuffer( root );
|
|
//Console.Out.WriteLine( "revIndex=" + tokenTypeToStreamIndexesMap );
|
|
p = 0; // buffer of nodes intialized now
|
|
}
|
|
|
|
public virtual void FillBuffer( object t )
|
|
{
|
|
bool nil = adaptor.IsNil( t );
|
|
if ( !nil )
|
|
{
|
|
nodes.Add( t ); // add this node
|
|
}
|
|
// add DOWN node if t has children
|
|
int n = adaptor.GetChildCount( t );
|
|
if ( !nil && n > 0 )
|
|
{
|
|
AddNavigationNode( TokenTypes.Down );
|
|
}
|
|
// and now add all its children
|
|
for ( int c = 0; c < n; c++ )
|
|
{
|
|
object child = adaptor.GetChild( t, c );
|
|
FillBuffer( child );
|
|
}
|
|
// add UP node if t has children
|
|
if ( !nil && n > 0 )
|
|
{
|
|
AddNavigationNode( TokenTypes.Up );
|
|
}
|
|
}
|
|
|
|
/** What is the stream index for node? 0..n-1
|
|
* Return -1 if node not found.
|
|
*/
|
|
protected virtual int GetNodeIndex( object node )
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
for ( int i = 0; i < nodes.Count; i++ )
|
|
{
|
|
object t = nodes[i];
|
|
if ( t == node )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/** As we flatten the tree, we use UP, DOWN nodes to represent
|
|
* the tree structure. When debugging we need unique nodes
|
|
* so instantiate new ones when uniqueNavigationNodes is true.
|
|
*/
|
|
protected virtual void AddNavigationNode( int ttype )
|
|
{
|
|
object navNode = null;
|
|
if ( ttype == TokenTypes.Down )
|
|
{
|
|
if ( UniqueNavigationNodes )
|
|
{
|
|
navNode = adaptor.Create( TokenTypes.Down, "DOWN" );
|
|
}
|
|
else
|
|
{
|
|
navNode = down;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( UniqueNavigationNodes )
|
|
{
|
|
navNode = adaptor.Create( TokenTypes.Up, "UP" );
|
|
}
|
|
else
|
|
{
|
|
navNode = up;
|
|
}
|
|
}
|
|
nodes.Add( navNode );
|
|
}
|
|
|
|
public virtual object this[int i]
|
|
{
|
|
get
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
throw new InvalidOperationException( "Cannot get the node at index i before the buffer is filled." );
|
|
}
|
|
return nodes[i];
|
|
}
|
|
}
|
|
|
|
public virtual object LT( int k )
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
if ( k == 0 )
|
|
{
|
|
return null;
|
|
}
|
|
if ( k < 0 )
|
|
{
|
|
return LB( -k );
|
|
}
|
|
//System.out.print("LT(p="+p+","+k+")=");
|
|
if ( ( p + k - 1 ) >= nodes.Count )
|
|
{
|
|
return eof;
|
|
}
|
|
return nodes[p + k - 1];
|
|
}
|
|
|
|
public virtual object GetCurrentSymbol()
|
|
{
|
|
return LT( 1 );
|
|
}
|
|
|
|
#if false
|
|
public virtual object getLastTreeNode()
|
|
{
|
|
int i = Index;
|
|
if ( i >= size() )
|
|
{
|
|
i--; // if at EOF, have to start one back
|
|
}
|
|
Console.Out.WriteLine( "start last node: " + i + " size==" + nodes.Count );
|
|
while ( i >= 0 &&
|
|
( adaptor.getType( this[i] ) == TokenTypes.EOF ||
|
|
adaptor.getType( this[i] ) == TokenTypes.UP ||
|
|
adaptor.getType( this[i] ) == TokenTypes.DOWN ) )
|
|
{
|
|
i--;
|
|
}
|
|
Console.Out.WriteLine( "stop at node: " + i + " " + nodes[i] );
|
|
return nodes[i];
|
|
}
|
|
#endif
|
|
|
|
/** <summary>Look backwards k nodes</summary> */
|
|
protected virtual object LB( int k )
|
|
{
|
|
if ( k == 0 )
|
|
{
|
|
return null;
|
|
}
|
|
if ( ( p - k ) < 0 )
|
|
{
|
|
return null;
|
|
}
|
|
return nodes[p - k];
|
|
}
|
|
|
|
public virtual void Consume()
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
p++;
|
|
}
|
|
|
|
public virtual int LA( int i )
|
|
{
|
|
return adaptor.GetType( LT( i ) );
|
|
}
|
|
|
|
public virtual int Mark()
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
lastMarker = Index;
|
|
return lastMarker;
|
|
}
|
|
|
|
public virtual void Release( int marker )
|
|
{
|
|
// no resources to release
|
|
}
|
|
|
|
public virtual int Index
|
|
{
|
|
get
|
|
{
|
|
return p;
|
|
}
|
|
}
|
|
|
|
public virtual void Rewind( int marker )
|
|
{
|
|
Seek( marker );
|
|
}
|
|
|
|
public virtual void Rewind()
|
|
{
|
|
Seek( lastMarker );
|
|
}
|
|
|
|
public virtual void Seek( int index )
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
p = index;
|
|
}
|
|
|
|
/** <summary>
|
|
* Make stream jump to a new location, saving old location.
|
|
* Switch back with pop().
|
|
* </summary>
|
|
*/
|
|
public virtual void Push( int index )
|
|
{
|
|
if ( calls == null )
|
|
{
|
|
calls = new Stack<int>();
|
|
}
|
|
calls.Push( p ); // save current index
|
|
Seek( index );
|
|
}
|
|
|
|
/** <summary>
|
|
* Seek back to previous index saved during last push() call.
|
|
* Return top of stack (return index).
|
|
* </summary>
|
|
*/
|
|
public virtual int Pop()
|
|
{
|
|
int ret = calls.Pop();
|
|
Seek( ret );
|
|
return ret;
|
|
}
|
|
|
|
public virtual void Reset()
|
|
{
|
|
p = 0;
|
|
lastMarker = 0;
|
|
if ( calls != null )
|
|
{
|
|
calls.Clear();
|
|
}
|
|
}
|
|
|
|
public virtual IEnumerator<object> Iterator()
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
|
|
return new StreamIterator( this );
|
|
}
|
|
|
|
// TREE REWRITE INTERFACE
|
|
|
|
public virtual void ReplaceChildren( object parent, int startChildIndex, int stopChildIndex, object t )
|
|
{
|
|
if ( parent != null )
|
|
{
|
|
adaptor.ReplaceChildren( parent, startChildIndex, stopChildIndex, t );
|
|
}
|
|
}
|
|
|
|
/** <summary>Used for testing, just return the token type stream</summary> */
|
|
public virtual string ToTokenTypeString()
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
StringBuilder buf = new StringBuilder();
|
|
for ( int i = 0; i < nodes.Count; i++ )
|
|
{
|
|
object t = nodes[i];
|
|
buf.Append( " " );
|
|
buf.Append( adaptor.GetType( t ) );
|
|
}
|
|
return buf.ToString();
|
|
}
|
|
|
|
/** <summary>Debugging</summary> */
|
|
public virtual string ToTokenString( int start, int stop )
|
|
{
|
|
if ( p == -1 )
|
|
{
|
|
FillBuffer();
|
|
}
|
|
StringBuilder buf = new StringBuilder();
|
|
for ( int i = start; i < nodes.Count && i <= stop; i++ )
|
|
{
|
|
object t = nodes[i];
|
|
buf.Append( " " );
|
|
buf.Append( adaptor.GetToken( t ) );
|
|
}
|
|
return buf.ToString();
|
|
}
|
|
|
|
public virtual string ToString( object start, object stop )
|
|
{
|
|
Console.Out.WriteLine( "toString" );
|
|
if ( start == null || stop == null )
|
|
{
|
|
return null;
|
|
}
|
|
if ( p == -1 )
|
|
{
|
|
throw new InvalidOperationException( "Buffer is not yet filled." );
|
|
}
|
|
//Console.Out.WriteLine( "stop: " + stop );
|
|
if ( start is CommonTree )
|
|
Console.Out.Write( "toString: " + ( (CommonTree)start ).Token + ", " );
|
|
else
|
|
Console.Out.WriteLine( start );
|
|
if ( stop is CommonTree )
|
|
Console.Out.WriteLine( ( (CommonTree)stop ).Token );
|
|
else
|
|
Console.Out.WriteLine( stop );
|
|
// if we have the token stream, use that to dump text in order
|
|
if ( tokens != null )
|
|
{
|
|
int beginTokenIndex = adaptor.GetTokenStartIndex( start );
|
|
int endTokenIndex = adaptor.GetTokenStopIndex( stop );
|
|
// if it's a tree, use start/stop index from start node
|
|
// else use token range from start/stop nodes
|
|
if ( adaptor.GetType( stop ) == TokenTypes.Up )
|
|
{
|
|
endTokenIndex = adaptor.GetTokenStopIndex( start );
|
|
}
|
|
else if ( adaptor.GetType( stop ) == TokenTypes.EndOfFile )
|
|
{
|
|
endTokenIndex = Count - 2; // don't use EOF
|
|
}
|
|
return tokens.ToString( beginTokenIndex, endTokenIndex );
|
|
}
|
|
// walk nodes looking for start
|
|
object t = null;
|
|
int i = 0;
|
|
for ( ; i < nodes.Count; i++ )
|
|
{
|
|
t = nodes[i];
|
|
if ( t == start )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// now walk until we see stop, filling string buffer with text
|
|
StringBuilder buf = new StringBuilder();
|
|
t = nodes[i];
|
|
while ( t != stop )
|
|
{
|
|
string text = adaptor.GetText( t );
|
|
if ( text == null )
|
|
{
|
|
text = " " + adaptor.GetType( t ).ToString();
|
|
}
|
|
buf.Append( text );
|
|
i++;
|
|
t = nodes[i];
|
|
}
|
|
// include stop node too
|
|
string text2 = adaptor.GetText( stop );
|
|
if ( text2 == null )
|
|
{
|
|
text2 = " " + adaptor.GetType( stop ).ToString();
|
|
}
|
|
buf.Append( text2 );
|
|
return buf.ToString();
|
|
}
|
|
}
|
|
}
|