/* * [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; /** A buffered stream of tree nodes. Nodes can be from a tree of ANY kind. * * 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 { BufferedTreeNodeStream _outer; int _index; public StreamIterator( BufferedTreeNodeStream outer ) { _outer = outer; _index = -1; } #region IEnumerator 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; /** 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. * * 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; /** Pull nodes from which tree? */ protected object root; /** IF this tree (root) was created from a token stream, track it. */ protected ITokenStream tokens; /** What tree adaptor was used to build these trees */ ITreeAdaptor adaptor; /** Reuse same DOWN, UP navigation nodes unless this is true */ bool uniqueNavigationNodes = false; /** The index into the nodes list of the current node (next node * to consume). If -1, nodes array not filled yet. */ protected int p = -1; /** Track the last mark() call result value for use in rewind(). */ protected int lastMarker; /** Stack of indexes used for push/pop calls */ protected Stack 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( 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 /** Look backwards k nodes */ 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; } /** * Make stream jump to a new location, saving old location. * Switch back with pop(). * */ public virtual void Push( int index ) { if ( calls == null ) { calls = new Stack(); } calls.Push( p ); // save current index Seek( index ); } /** * Seek back to previous index saved during last push() call. * Return top of stack (return index). * */ 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 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 ); } } /** Used for testing, just return the token type stream */ 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(); } /** Debugging */ 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(); } } }