576 lines
19 KiB
C#
576 lines
19 KiB
C#
/*
|
|
* [The "BSD license"]
|
|
* Copyright (c) 2011 Terence Parr
|
|
* All rights reserved.
|
|
*
|
|
* Conversion to C#:
|
|
* Copyright (c) 2011 Sam Harwell, Tunnel Vision Laboratories, LLC
|
|
* 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;
|
|
using System.Collections.Generic;
|
|
|
|
using StringBuilder = System.Text.StringBuilder;
|
|
|
|
/** <summary>
|
|
* A generic tree implementation with no payload. You must subclass to
|
|
* actually have any user data. ANTLR v3 uses a list of children approach
|
|
* instead of the child-sibling approach in v2. A flat tree (a list) is
|
|
* an empty node whose children represent the list. An empty, but
|
|
* non-null node is called "nil".
|
|
* </summary>
|
|
*/
|
|
[System.Serializable]
|
|
[System.Diagnostics.DebuggerTypeProxy(typeof(AntlrRuntime_BaseTreeDebugView))]
|
|
public abstract class BaseTree : ITree
|
|
{
|
|
private IList<ITree> _children;
|
|
|
|
public BaseTree()
|
|
{
|
|
}
|
|
|
|
/** <summary>
|
|
* Create a new node from an existing node does nothing for BaseTree
|
|
* as there are no fields other than the children list, which cannot
|
|
* be copied as the children are not considered part of this node.
|
|
* </summary>
|
|
*/
|
|
public BaseTree( ITree node )
|
|
{
|
|
}
|
|
|
|
/** <summary>
|
|
* Get the children internal List; note that if you directly mess with
|
|
* the list, do so at your own risk.
|
|
* </summary>
|
|
*/
|
|
public virtual IList<ITree> Children
|
|
{
|
|
get
|
|
{
|
|
return _children;
|
|
}
|
|
|
|
private set
|
|
{
|
|
_children = value;
|
|
}
|
|
}
|
|
|
|
#region ITree Members
|
|
|
|
public virtual int ChildCount
|
|
{
|
|
get
|
|
{
|
|
if ( Children == null )
|
|
return 0;
|
|
|
|
return Children.Count;
|
|
}
|
|
}
|
|
|
|
/** <summary>BaseTree doesn't track parent pointers.</summary> */
|
|
public virtual ITree Parent
|
|
{
|
|
get
|
|
{
|
|
return null;
|
|
}
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
/** <summary>BaseTree doesn't track child indexes.</summary> */
|
|
public virtual int ChildIndex
|
|
{
|
|
get
|
|
{
|
|
return 0;
|
|
}
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
public virtual bool IsNil
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public abstract int TokenStartIndex
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public abstract int TokenStopIndex
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public abstract int Type
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public abstract string Text
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public virtual int Line
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public virtual int CharPositionInLine
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
#endregion
|
|
|
|
public virtual ITree GetChild( int i )
|
|
{
|
|
if (i < 0)
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
if ( Children == null || i >= Children.Count )
|
|
return null;
|
|
|
|
return Children[i];
|
|
}
|
|
|
|
public virtual ITree GetFirstChildWithType( int type )
|
|
{
|
|
foreach ( ITree child in Children )
|
|
{
|
|
if ( child.Type == type )
|
|
return child;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/** <summary>Add t as child of this node.</summary>
|
|
*
|
|
* <remarks>
|
|
* Warning: if t has no children, but child does
|
|
* and child isNil then this routine moves children to t via
|
|
* t.children = child.children; i.e., without copying the array.
|
|
* </remarks>
|
|
*/
|
|
public virtual void AddChild( ITree t )
|
|
{
|
|
//System.out.println("add child "+t.toStringTree()+" "+this.toStringTree());
|
|
//System.out.println("existing children: "+children);
|
|
if ( t == null )
|
|
{
|
|
return; // do nothing upon addChild(null)
|
|
}
|
|
if ( t.IsNil )
|
|
{
|
|
// t is an empty node possibly with children
|
|
BaseTree childTree = t as BaseTree;
|
|
if ( childTree != null && this.Children != null && this.Children == childTree.Children )
|
|
{
|
|
throw new Exception( "attempt to add child list to itself" );
|
|
}
|
|
// just add all of childTree's children to this
|
|
if ( t.ChildCount > 0 )
|
|
{
|
|
if ( this.Children != null || childTree == null )
|
|
{
|
|
if ( this.Children == null )
|
|
this.Children = CreateChildrenList();
|
|
|
|
// must copy, this has children already
|
|
int n = t.ChildCount;
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
ITree c = t.GetChild( i );
|
|
this.Children.Add( c );
|
|
// handle double-link stuff for each child of nil root
|
|
c.Parent = this;
|
|
c.ChildIndex = Children.Count - 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no children for this but t is a BaseTree with children;
|
|
// just set pointer call general freshener routine
|
|
this.Children = childTree.Children;
|
|
this.FreshenParentAndChildIndexes();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// child is not nil (don't care about children)
|
|
if ( Children == null )
|
|
{
|
|
Children = CreateChildrenList(); // create children list on demand
|
|
}
|
|
Children.Add( t );
|
|
t.Parent = this;
|
|
t.ChildIndex = Children.Count - 1;
|
|
}
|
|
// System.out.println("now children are: "+children);
|
|
}
|
|
|
|
/** <summary>Add all elements of kids list as children of this node</summary> */
|
|
public virtual void AddChildren( IEnumerable<ITree> kids )
|
|
{
|
|
if (kids == null)
|
|
throw new ArgumentNullException("kids");
|
|
|
|
foreach ( ITree t in kids )
|
|
AddChild( t );
|
|
}
|
|
|
|
public virtual void SetChild( int i, ITree t )
|
|
{
|
|
if (i < 0)
|
|
throw new ArgumentOutOfRangeException("i");
|
|
|
|
if ( t == null )
|
|
{
|
|
return;
|
|
}
|
|
if ( t.IsNil )
|
|
{
|
|
throw new ArgumentException( "Can't set single child to a list" );
|
|
}
|
|
if ( Children == null )
|
|
{
|
|
Children = CreateChildrenList();
|
|
}
|
|
Children[i] = t;
|
|
t.Parent = this;
|
|
t.ChildIndex = i;
|
|
}
|
|
|
|
/** Insert child t at child position i (0..n-1) by shifting children
|
|
* i+1..n-1 to the right one position. Set parent / indexes properly
|
|
* but does NOT collapse nil-rooted t's that come in here like addChild.
|
|
*/
|
|
public virtual void InsertChild(int i, ITree t)
|
|
{
|
|
if (i < 0)
|
|
throw new ArgumentOutOfRangeException("i");
|
|
if (i > ChildCount)
|
|
throw new ArgumentException();
|
|
|
|
if (i == ChildCount)
|
|
{
|
|
AddChild(t);
|
|
return;
|
|
}
|
|
|
|
Children.Insert(i, t);
|
|
|
|
// walk others to increment their child indexes
|
|
// set index, parent of this one too
|
|
this.FreshenParentAndChildIndexes(i);
|
|
}
|
|
|
|
public virtual object DeleteChild( int i )
|
|
{
|
|
if (i < 0)
|
|
throw new ArgumentOutOfRangeException("i");
|
|
if (i >= ChildCount)
|
|
throw new ArgumentException();
|
|
|
|
if ( Children == null )
|
|
return null;
|
|
|
|
ITree killed = Children[i];
|
|
Children.RemoveAt( i );
|
|
// walk rest and decrement their child indexes
|
|
this.FreshenParentAndChildIndexes( i );
|
|
return killed;
|
|
}
|
|
|
|
/** <summary>
|
|
* Delete children from start to stop and replace with t even if t is
|
|
* a list (nil-root tree). num of children can increase or decrease.
|
|
* For huge child lists, inserting children can force walking rest of
|
|
* children to set their childindex; could be slow.
|
|
* </summary>
|
|
*/
|
|
public virtual void ReplaceChildren( int startChildIndex, int stopChildIndex, object t )
|
|
{
|
|
if (startChildIndex < 0)
|
|
throw new ArgumentOutOfRangeException();
|
|
if (stopChildIndex < 0)
|
|
throw new ArgumentOutOfRangeException();
|
|
if (t == null)
|
|
throw new ArgumentNullException("t");
|
|
if (stopChildIndex < startChildIndex)
|
|
throw new ArgumentException();
|
|
|
|
/*
|
|
System.out.println("replaceChildren "+startChildIndex+", "+stopChildIndex+
|
|
" with "+((BaseTree)t).toStringTree());
|
|
System.out.println("in="+toStringTree());
|
|
*/
|
|
if ( Children == null )
|
|
{
|
|
throw new ArgumentException( "indexes invalid; no children in list" );
|
|
}
|
|
int replacingHowMany = stopChildIndex - startChildIndex + 1;
|
|
int replacingWithHowMany;
|
|
ITree newTree = (ITree)t;
|
|
IList<ITree> newChildren = null;
|
|
// normalize to a list of children to add: newChildren
|
|
if ( newTree.IsNil )
|
|
{
|
|
BaseTree baseTree = newTree as BaseTree;
|
|
if ( baseTree != null && baseTree.Children != null )
|
|
{
|
|
newChildren = baseTree.Children;
|
|
}
|
|
else
|
|
{
|
|
newChildren = CreateChildrenList();
|
|
int n = newTree.ChildCount;
|
|
for ( int i = 0; i < n; i++ )
|
|
newChildren.Add( newTree.GetChild( i ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newChildren = new List<ITree>( 1 );
|
|
newChildren.Add( newTree );
|
|
}
|
|
replacingWithHowMany = newChildren.Count;
|
|
int numNewChildren = newChildren.Count;
|
|
int delta = replacingHowMany - replacingWithHowMany;
|
|
// if same number of nodes, do direct replace
|
|
if ( delta == 0 )
|
|
{
|
|
int j = 0; // index into new children
|
|
for ( int i = startChildIndex; i <= stopChildIndex; i++ )
|
|
{
|
|
ITree child = newChildren[j];
|
|
Children[i] = child;
|
|
child.Parent = this;
|
|
child.ChildIndex = i;
|
|
j++;
|
|
}
|
|
}
|
|
else if ( delta > 0 )
|
|
{
|
|
// fewer new nodes than there were
|
|
// set children and then delete extra
|
|
for ( int j = 0; j < numNewChildren; j++ )
|
|
{
|
|
Children[startChildIndex + j] = newChildren[j];
|
|
}
|
|
int indexToDelete = startChildIndex + numNewChildren;
|
|
for ( int c = indexToDelete; c <= stopChildIndex; c++ )
|
|
{
|
|
// delete same index, shifting everybody down each time
|
|
Children.RemoveAt( indexToDelete );
|
|
}
|
|
FreshenParentAndChildIndexes( startChildIndex );
|
|
}
|
|
else
|
|
{
|
|
// more new nodes than were there before
|
|
// fill in as many children as we can (replacingHowMany) w/o moving data
|
|
for ( int j = 0; j < replacingHowMany; j++ )
|
|
{
|
|
Children[startChildIndex + j] = newChildren[j];
|
|
}
|
|
int numToInsert = replacingWithHowMany - replacingHowMany;
|
|
for ( int j = replacingHowMany; j < replacingWithHowMany; j++ )
|
|
{
|
|
Children.Insert( startChildIndex + j, newChildren[j] );
|
|
}
|
|
FreshenParentAndChildIndexes( startChildIndex );
|
|
}
|
|
//System.out.println("out="+toStringTree());
|
|
}
|
|
|
|
/** <summary>Override in a subclass to change the impl of children list</summary> */
|
|
protected virtual IList<ITree> CreateChildrenList()
|
|
{
|
|
return new List<ITree>();
|
|
}
|
|
|
|
/** <summary>Set the parent and child index values for all child of t</summary> */
|
|
public virtual void FreshenParentAndChildIndexes()
|
|
{
|
|
FreshenParentAndChildIndexes( 0 );
|
|
}
|
|
|
|
public virtual void FreshenParentAndChildIndexes( int offset )
|
|
{
|
|
int n = ChildCount;
|
|
for ( int c = offset; c < n; c++ )
|
|
{
|
|
ITree child = GetChild( c );
|
|
child.ChildIndex = c;
|
|
child.Parent = this;
|
|
}
|
|
}
|
|
|
|
public virtual void FreshenParentAndChildIndexesDeeply()
|
|
{
|
|
FreshenParentAndChildIndexesDeeply(0);
|
|
}
|
|
|
|
public virtual void FreshenParentAndChildIndexesDeeply(int offset)
|
|
{
|
|
int n = ChildCount;
|
|
for (int c = offset; c < n; c++)
|
|
{
|
|
ITree child = GetChild(c);
|
|
child.ChildIndex = c;
|
|
child.Parent = this;
|
|
BaseTree baseTree = child as BaseTree;
|
|
if (baseTree != null)
|
|
baseTree.FreshenParentAndChildIndexesDeeply();
|
|
}
|
|
}
|
|
|
|
public virtual void SanityCheckParentAndChildIndexes()
|
|
{
|
|
SanityCheckParentAndChildIndexes( null, -1 );
|
|
}
|
|
|
|
public virtual void SanityCheckParentAndChildIndexes( ITree parent, int i )
|
|
{
|
|
if ( parent != this.Parent )
|
|
{
|
|
throw new InvalidOperationException( "parents don't match; expected " + parent + " found " + this.Parent );
|
|
}
|
|
if ( i != this.ChildIndex )
|
|
{
|
|
throw new InvalidOperationException( "child indexes don't match; expected " + i + " found " + this.ChildIndex );
|
|
}
|
|
int n = this.ChildCount;
|
|
for ( int c = 0; c < n; c++ )
|
|
{
|
|
BaseTree child = (BaseTree)this.GetChild( c );
|
|
child.SanityCheckParentAndChildIndexes( this, c );
|
|
}
|
|
}
|
|
|
|
/** <summary>Walk upwards looking for ancestor with this token type.</summary> */
|
|
public virtual bool HasAncestor( int ttype )
|
|
{
|
|
return GetAncestor( ttype ) != null;
|
|
}
|
|
|
|
/** <summary>Walk upwards and get first ancestor with this token type.</summary> */
|
|
public virtual ITree GetAncestor( int ttype )
|
|
{
|
|
ITree t = this;
|
|
t = t.Parent;
|
|
while ( t != null )
|
|
{
|
|
if ( t.Type == ttype )
|
|
return t;
|
|
t = t.Parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** <summary>
|
|
* Return a list of all ancestors of this node. The first node of
|
|
* list is the root and the last is the parent of this node.
|
|
* </summary>
|
|
*/
|
|
public virtual IList<ITree> GetAncestors()
|
|
{
|
|
if ( Parent == null )
|
|
return null;
|
|
|
|
List<ITree> ancestors = new List<ITree>();
|
|
ITree t = this;
|
|
t = t.Parent;
|
|
while ( t != null )
|
|
{
|
|
ancestors.Insert( 0, t ); // insert at start
|
|
t = t.Parent;
|
|
}
|
|
return ancestors;
|
|
}
|
|
|
|
/** <summary>Print out a whole tree not just a node</summary> */
|
|
public virtual string ToStringTree()
|
|
{
|
|
if ( Children == null || Children.Count == 0 )
|
|
{
|
|
return this.ToString();
|
|
}
|
|
StringBuilder buf = new StringBuilder();
|
|
if ( !IsNil )
|
|
{
|
|
buf.Append( "(" );
|
|
buf.Append( this.ToString() );
|
|
buf.Append( ' ' );
|
|
}
|
|
for ( int i = 0; Children != null && i < Children.Count; i++ )
|
|
{
|
|
ITree t = Children[i];
|
|
if ( i > 0 )
|
|
{
|
|
buf.Append( ' ' );
|
|
}
|
|
buf.Append( t.ToStringTree() );
|
|
}
|
|
if ( !IsNil )
|
|
{
|
|
buf.Append( ")" );
|
|
}
|
|
return buf.ToString();
|
|
}
|
|
|
|
/** <summary>Override to say how a node (not a tree) should look as text</summary> */
|
|
public override abstract string ToString();
|
|
|
|
#region Tree Members
|
|
public abstract ITree DupNode();
|
|
#endregion
|
|
}
|
|
}
|