518 lines
17 KiB
C#
518 lines
17 KiB
C#
/*
|
|
* [The "BSD licence"]
|
|
* Copyright (c) 2011 Terence Parr
|
|
* All rights reserved.
|
|
*
|
|
* Conversion to C#:
|
|
* Copyright (c) 2011 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 ArgumentNullException = System.ArgumentNullException;
|
|
using Exception = System.Exception;
|
|
using IDictionary = System.Collections.IDictionary;
|
|
using NotSupportedException = System.NotSupportedException;
|
|
|
|
/** <summary>A TreeAdaptor that works with any Tree implementation.</summary> */
|
|
public abstract class BaseTreeAdaptor : ITreeAdaptor
|
|
{
|
|
/** <summary>
|
|
* System.identityHashCode() is not always unique; we have to
|
|
* track ourselves. That's ok, it's only for debugging, though it's
|
|
* expensive: we have to create a hashtable with all tree nodes in it.
|
|
* </summary>
|
|
*/
|
|
protected IDictionary<object, int> treeToUniqueIDMap;
|
|
protected int uniqueNodeID = 1;
|
|
|
|
public virtual object Nil()
|
|
{
|
|
return Create( null );
|
|
}
|
|
|
|
/** <summary>
|
|
* Create tree node that holds the start and stop tokens associated
|
|
* with an error.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* If you specify your own kind of tree nodes, you will likely have to
|
|
* override this method. CommonTree returns Token.INVALID_TOKEN_TYPE
|
|
* if no token payload but you might have to set token type for diff
|
|
* node type.
|
|
*
|
|
* You don't have to subclass CommonErrorNode; you will likely need to
|
|
* subclass your own tree node class to avoid class cast exception.
|
|
* </remarks>
|
|
*/
|
|
public virtual object ErrorNode( ITokenStream input, IToken start, IToken stop,
|
|
RecognitionException e )
|
|
{
|
|
CommonErrorNode t = new CommonErrorNode( input, start, stop, e );
|
|
//System.out.println("returning error node '"+t+"' @index="+input.index());
|
|
return t;
|
|
}
|
|
|
|
public virtual bool IsNil( object tree )
|
|
{
|
|
return ( (ITree)tree ).IsNil;
|
|
}
|
|
|
|
public virtual object DupNode(int type, object treeNode)
|
|
{
|
|
object t = DupNode(treeNode);
|
|
SetType(t, type);
|
|
return t;
|
|
}
|
|
|
|
public virtual object DupNode(object treeNode, string text)
|
|
{
|
|
object t = DupNode(treeNode);
|
|
SetText(t, text);
|
|
return t;
|
|
}
|
|
|
|
public virtual object DupNode(int type, object treeNode, string text)
|
|
{
|
|
object t = DupNode(treeNode);
|
|
SetType(t, type);
|
|
SetText(t, text);
|
|
return t;
|
|
}
|
|
|
|
public virtual object DupTree( object tree )
|
|
{
|
|
return DupTree( tree, null );
|
|
}
|
|
|
|
/** <summary>
|
|
* This is generic in the sense that it will work with any kind of
|
|
* tree (not just ITree interface). It invokes the adaptor routines
|
|
* not the tree node routines to do the construction.
|
|
* </summary>
|
|
*/
|
|
public virtual object DupTree( object t, object parent )
|
|
{
|
|
if ( t == null )
|
|
{
|
|
return null;
|
|
}
|
|
object newTree = DupNode( t );
|
|
// ensure new subtree root has parent/child index set
|
|
SetChildIndex( newTree, GetChildIndex( t ) ); // same index in new tree
|
|
SetParent( newTree, parent );
|
|
int n = GetChildCount( t );
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
object child = GetChild( t, i );
|
|
object newSubTree = DupTree( child, t );
|
|
AddChild( newTree, newSubTree );
|
|
}
|
|
return newTree;
|
|
}
|
|
|
|
/** <summary>
|
|
* Add a child to the tree t. If child is a flat tree (a list), make all
|
|
* in list children of t. Warning: if t has no children, but child does
|
|
* and child isNil then you can decide it is ok to move children to t via
|
|
* t.children = child.children; i.e., without copying the array. Just
|
|
* make sure that this is consistent with have the user will build
|
|
* ASTs.
|
|
* </summary>
|
|
*/
|
|
public virtual void AddChild( object t, object child )
|
|
{
|
|
if ( t != null && child != null )
|
|
{
|
|
( (ITree)t ).AddChild( (ITree)child );
|
|
}
|
|
}
|
|
|
|
/** <summary>
|
|
* If oldRoot is a nil root, just copy or move the children to newRoot.
|
|
* If not a nil root, make oldRoot a child of newRoot.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* old=^(nil a b c), new=r yields ^(r a b c)
|
|
* old=^(a b c), new=r yields ^(r ^(a b c))
|
|
*
|
|
* If newRoot is a nil-rooted single child tree, use the single
|
|
* child as the new root node.
|
|
*
|
|
* old=^(nil a b c), new=^(nil r) yields ^(r a b c)
|
|
* old=^(a b c), new=^(nil r) yields ^(r ^(a b c))
|
|
*
|
|
* If oldRoot was null, it's ok, just return newRoot (even if isNil).
|
|
*
|
|
* old=null, new=r yields r
|
|
* old=null, new=^(nil r) yields ^(nil r)
|
|
*
|
|
* Return newRoot. Throw an exception if newRoot is not a
|
|
* simple node or nil root with a single child node--it must be a root
|
|
* node. If newRoot is ^(nil x) return x as newRoot.
|
|
*
|
|
* Be advised that it's ok for newRoot to point at oldRoot's
|
|
* children; i.e., you don't have to copy the list. We are
|
|
* constructing these nodes so we should have this control for
|
|
* efficiency.
|
|
* </remarks>
|
|
*/
|
|
public virtual object BecomeRoot( object newRoot, object oldRoot )
|
|
{
|
|
//System.out.println("becomeroot new "+newRoot.toString()+" old "+oldRoot);
|
|
ITree newRootTree = (ITree)newRoot;
|
|
ITree oldRootTree = (ITree)oldRoot;
|
|
if ( oldRoot == null )
|
|
{
|
|
return newRoot;
|
|
}
|
|
// handle ^(nil real-node)
|
|
if ( newRootTree.IsNil )
|
|
{
|
|
int nc = newRootTree.ChildCount;
|
|
if ( nc == 1 )
|
|
newRootTree = (ITree)newRootTree.GetChild( 0 );
|
|
else if ( nc > 1 )
|
|
{
|
|
// TODO: make tree run time exceptions hierarchy
|
|
throw new Exception( "more than one node as root (TODO: make exception hierarchy)" );
|
|
}
|
|
}
|
|
// add oldRoot to newRoot; addChild takes care of case where oldRoot
|
|
// is a flat list (i.e., nil-rooted tree). All children of oldRoot
|
|
// are added to newRoot.
|
|
newRootTree.AddChild( oldRootTree );
|
|
return newRootTree;
|
|
}
|
|
|
|
/** <summary>Transform ^(nil x) to x and nil to null</summary> */
|
|
public virtual object RulePostProcessing( object root )
|
|
{
|
|
//System.out.println("rulePostProcessing: "+((Tree)root).toStringTree());
|
|
ITree r = (ITree)root;
|
|
if ( r != null && r.IsNil )
|
|
{
|
|
if ( r.ChildCount == 0 )
|
|
{
|
|
r = null;
|
|
}
|
|
else if ( r.ChildCount == 1 )
|
|
{
|
|
r = (ITree)r.GetChild( 0 );
|
|
// whoever invokes rule will set parent and child index
|
|
r.Parent = null;
|
|
r.ChildIndex = -1;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
public virtual object BecomeRoot( IToken newRoot, object oldRoot )
|
|
{
|
|
return BecomeRoot( Create( newRoot ), oldRoot );
|
|
}
|
|
|
|
public virtual object Create( int tokenType, IToken fromToken )
|
|
{
|
|
fromToken = CreateToken( fromToken );
|
|
fromToken.Type = tokenType;
|
|
object t = Create( fromToken );
|
|
return t;
|
|
}
|
|
|
|
public virtual object Create( int tokenType, IToken fromToken, string text )
|
|
{
|
|
if ( fromToken == null )
|
|
return Create( tokenType, text );
|
|
|
|
fromToken = CreateToken( fromToken );
|
|
fromToken.Type = tokenType;
|
|
fromToken.Text = text;
|
|
object result = Create(fromToken);
|
|
return result;
|
|
}
|
|
|
|
public virtual object Create(IToken fromToken, string text)
|
|
{
|
|
if (fromToken == null)
|
|
throw new ArgumentNullException("fromToken");
|
|
|
|
fromToken = CreateToken(fromToken);
|
|
fromToken.Text = text;
|
|
object result = Create(fromToken);
|
|
return result;
|
|
}
|
|
|
|
public virtual object Create( int tokenType, string text )
|
|
{
|
|
IToken fromToken = CreateToken( tokenType, text );
|
|
object t = Create( fromToken );
|
|
return t;
|
|
}
|
|
|
|
public virtual int GetType( object t )
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return TokenTypes.Invalid;
|
|
|
|
return tree.Type;
|
|
}
|
|
|
|
public virtual void SetType( object t, int type )
|
|
{
|
|
throw new NotSupportedException( "don't know enough about Tree node" );
|
|
}
|
|
|
|
public virtual string GetText( object t )
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return null;
|
|
|
|
return tree.Text;
|
|
}
|
|
|
|
public virtual void SetText( object t, string text )
|
|
{
|
|
throw new NotSupportedException( "don't know enough about Tree node" );
|
|
}
|
|
|
|
public virtual object GetChild( object t, int i )
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return null;
|
|
|
|
return tree.GetChild(i);
|
|
}
|
|
|
|
public virtual void SetChild( object t, int i, object child )
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return;
|
|
|
|
ITree childTree = GetTree(child);
|
|
tree.SetChild(i, childTree);
|
|
}
|
|
|
|
public virtual object DeleteChild( object t, int i )
|
|
{
|
|
return ( (ITree)t ).DeleteChild( i );
|
|
}
|
|
|
|
public virtual int GetChildCount( object t )
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return 0;
|
|
|
|
return tree.ChildCount;
|
|
}
|
|
|
|
public virtual int GetUniqueID( object node )
|
|
{
|
|
if ( treeToUniqueIDMap == null )
|
|
{
|
|
treeToUniqueIDMap = new Dictionary<object, int>();
|
|
}
|
|
int id;
|
|
if ( treeToUniqueIDMap.TryGetValue( node, out id ) )
|
|
return id;
|
|
|
|
id = uniqueNodeID;
|
|
treeToUniqueIDMap[node] = id;
|
|
uniqueNodeID++;
|
|
return id;
|
|
// GC makes these nonunique:
|
|
// return System.identityHashCode(node);
|
|
}
|
|
|
|
/** <summary>
|
|
* Tell me how to create a token for use with imaginary token nodes.
|
|
* For example, there is probably no input symbol associated with imaginary
|
|
* token DECL, but you need to create it as a payload or whatever for
|
|
* the DECL node as in ^(DECL type ID).
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* If you care what the token payload objects' type is, you should
|
|
* override this method and any other createToken variant.
|
|
* </remarks>
|
|
*/
|
|
public abstract IToken CreateToken( int tokenType, string text );
|
|
|
|
/** <summary>
|
|
* Tell me how to create a token for use with imaginary token nodes.
|
|
* For example, there is probably no input symbol associated with imaginary
|
|
* token DECL, but you need to create it as a payload or whatever for
|
|
* the DECL node as in ^(DECL type ID).
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* This is a variant of createToken where the new token is derived from
|
|
* an actual real input token. Typically this is for converting '{'
|
|
* tokens to BLOCK etc... You'll see
|
|
*
|
|
* r : lc='{' ID+ '}' -> ^(BLOCK[$lc] ID+) ;
|
|
*
|
|
* If you care what the token payload objects' type is, you should
|
|
* override this method and any other createToken variant.
|
|
* </remarks>
|
|
*/
|
|
public abstract IToken CreateToken( IToken fromToken );
|
|
|
|
public abstract object Create( IToken payload );
|
|
|
|
/** <summary>
|
|
* Duplicate a node. This is part of the factory;
|
|
* override if you want another kind of node to be built.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* I could use reflection to prevent having to override this
|
|
* but reflection is slow.
|
|
* </remarks>
|
|
*/
|
|
public virtual object DupNode(object treeNode)
|
|
{
|
|
ITree tree = GetTree(treeNode);
|
|
if (tree == null)
|
|
return null;
|
|
|
|
return tree.DupNode();
|
|
}
|
|
|
|
public abstract IToken GetToken( object t );
|
|
|
|
/** <summary>
|
|
* Track start/stop token for subtree root created for a rule.
|
|
* Only works with Tree nodes. For rules that match nothing,
|
|
* seems like this will yield start=i and stop=i-1 in a nil node.
|
|
* Might be useful info so I'll not force to be i..i.
|
|
* </summary>
|
|
*/
|
|
public virtual void SetTokenBoundaries(object t, IToken startToken, IToken stopToken)
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return;
|
|
|
|
int start = 0;
|
|
int stop = 0;
|
|
|
|
if (startToken != null)
|
|
start = startToken.TokenIndex;
|
|
if (stopToken != null)
|
|
stop = stopToken.TokenIndex;
|
|
|
|
tree.TokenStartIndex = start;
|
|
tree.TokenStopIndex = stop;
|
|
}
|
|
|
|
public virtual int GetTokenStartIndex(object t)
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return -1;
|
|
|
|
return tree.TokenStartIndex;
|
|
}
|
|
|
|
public virtual int GetTokenStopIndex(object t)
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return -1;
|
|
|
|
return tree.TokenStopIndex;
|
|
}
|
|
|
|
public virtual object GetParent(object t)
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return null;
|
|
|
|
return tree.Parent;
|
|
}
|
|
|
|
public virtual void SetParent(object t, object parent)
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return;
|
|
|
|
ITree parentTree = GetTree(parent);
|
|
tree.Parent = parentTree;
|
|
}
|
|
|
|
public virtual int GetChildIndex(object t)
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return 0;
|
|
|
|
return tree.ChildIndex;
|
|
}
|
|
|
|
public virtual void SetChildIndex(object t, int index)
|
|
{
|
|
ITree tree = GetTree(t);
|
|
if (tree == null)
|
|
return;
|
|
|
|
tree.ChildIndex = index;
|
|
}
|
|
|
|
public virtual void ReplaceChildren(object parent, int startChildIndex, int stopChildIndex, object t)
|
|
{
|
|
ITree tree = GetTree(parent);
|
|
if (tree == null)
|
|
return;
|
|
|
|
tree.ReplaceChildren(startChildIndex, stopChildIndex, t);
|
|
}
|
|
|
|
protected virtual ITree GetTree(object t)
|
|
{
|
|
if (t == null)
|
|
return null;
|
|
|
|
ITree tree = t as ITree;
|
|
if (tree == null)
|
|
throw new NotSupportedException();
|
|
|
|
return tree;
|
|
}
|
|
}
|
|
}
|