/* * [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(); } } }