blob: 3b5a01ecc5b60ccbafc7e5e091fb54ad7c0a5f4b [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.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();
}
}
}