You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
558 lines
18 KiB
558 lines
18 KiB
/*
|
|
* [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();
|
|
}
|
|
}
|
|
}
|