blob: d77e031d9c75ae24a2014f90d9a01949aebcd330 [file] [log] [blame]
/*
* [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;
}
}
}