737 lines
26 KiB
C#
737 lines
26 KiB
C#
/*
|
|
* [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.
|
|
*/
|
|
|
|
// TODO: build indexes for wizard
|
|
//#define BUILD_INDEXES
|
|
|
|
namespace Antlr.Runtime.Tree
|
|
{
|
|
using System.Collections.Generic;
|
|
|
|
using IList = System.Collections.IList;
|
|
#if BUILD_INDEXES
|
|
using IDictionary = System.Collections.IDictionary;
|
|
#endif
|
|
|
|
/** <summary>
|
|
* Build and navigate trees with this object. Must know about the names
|
|
* of tokens so you have to pass in a map or array of token names (from which
|
|
* this class can build the map). I.e., Token DECL means nothing unless the
|
|
* class can translate it to a token type.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* In order to create nodes and navigate, this class needs a TreeAdaptor.
|
|
*
|
|
* This class can build a token type -> node index for repeated use or for
|
|
* iterating over the various nodes with a particular type.
|
|
*
|
|
* This class works in conjunction with the TreeAdaptor rather than moving
|
|
* all this functionality into the adaptor. An adaptor helps build and
|
|
* navigate trees using methods. This class helps you do it with string
|
|
* patterns like "(A B C)". You can create a tree from that pattern or
|
|
* match subtrees against it.
|
|
* </remarks>
|
|
*/
|
|
public class TreeWizard
|
|
{
|
|
protected ITreeAdaptor adaptor;
|
|
protected IDictionary<string, int> tokenNameToTypeMap;
|
|
|
|
public interface IContextVisitor
|
|
{
|
|
// TODO: should this be called visit or something else?
|
|
void Visit( object t, object parent, int childIndex, IDictionary<string, object> labels );
|
|
}
|
|
|
|
public abstract class Visitor : IContextVisitor
|
|
{
|
|
public virtual void Visit( object t, object parent, int childIndex, IDictionary<string, object> labels )
|
|
{
|
|
Visit( t );
|
|
}
|
|
public abstract void Visit( object t );
|
|
}
|
|
|
|
class ActionVisitor : Visitor
|
|
{
|
|
System.Action<object> _action;
|
|
|
|
public ActionVisitor( System.Action<object> action )
|
|
{
|
|
_action = action;
|
|
}
|
|
|
|
public override void Visit( object t )
|
|
{
|
|
_action( t );
|
|
}
|
|
}
|
|
|
|
/** <summary>
|
|
* When using %label:TOKENNAME in a tree for parse(), we must
|
|
* track the label.
|
|
* </summary>
|
|
*/
|
|
public class TreePattern : CommonTree
|
|
{
|
|
public string label;
|
|
public bool hasTextArg;
|
|
public TreePattern( IToken payload ) :
|
|
base( payload )
|
|
{
|
|
}
|
|
public override string ToString()
|
|
{
|
|
if ( label != null )
|
|
{
|
|
return "%" + label + ":"; //+ base.ToString();
|
|
}
|
|
else
|
|
{
|
|
return base.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class WildcardTreePattern : TreePattern
|
|
{
|
|
public WildcardTreePattern( IToken payload ) :
|
|
base( payload )
|
|
{
|
|
}
|
|
}
|
|
|
|
/** <summary>This adaptor creates TreePattern objects for use during scan()</summary> */
|
|
public class TreePatternTreeAdaptor : CommonTreeAdaptor
|
|
{
|
|
public override object Create( IToken payload )
|
|
{
|
|
return new TreePattern( payload );
|
|
}
|
|
}
|
|
|
|
#if BUILD_INDEXES
|
|
// TODO: build indexes for the wizard
|
|
|
|
/** <summary>
|
|
* During fillBuffer(), we can make a reverse index from a set
|
|
* of token types of interest to the list of indexes into the
|
|
* node stream. This lets us convert a node pointer to a
|
|
* stream index semi-efficiently for a list of interesting
|
|
* nodes such as function definition nodes (you'll want to seek
|
|
* to their bodies for an interpreter). Also useful for doing
|
|
* dynamic searches; i.e., go find me all PLUS nodes.
|
|
* </summary>
|
|
*/
|
|
protected IDictionary<int, IList<int>> tokenTypeToStreamIndexesMap;
|
|
|
|
/** <summary>
|
|
* If tokenTypesToReverseIndex set to INDEX_ALL then indexing
|
|
* occurs for all token types.
|
|
* </summary>
|
|
*/
|
|
public static readonly HashSet<int> INDEX_ALL = new HashSet<int>();
|
|
|
|
/** <summary>
|
|
* A set of token types user would like to index for faster lookup.
|
|
* If this is INDEX_ALL, then all token types are tracked. If null,
|
|
* then none are indexed.
|
|
* </summary>
|
|
*/
|
|
protected HashSet<int> tokenTypesToReverseIndex = null;
|
|
#endif
|
|
|
|
public TreeWizard( ITreeAdaptor adaptor )
|
|
{
|
|
this.adaptor = adaptor;
|
|
}
|
|
|
|
public TreeWizard( ITreeAdaptor adaptor, IDictionary<string, int> tokenNameToTypeMap )
|
|
{
|
|
this.adaptor = adaptor;
|
|
this.tokenNameToTypeMap = tokenNameToTypeMap;
|
|
}
|
|
|
|
public TreeWizard( ITreeAdaptor adaptor, string[] tokenNames )
|
|
{
|
|
this.adaptor = adaptor;
|
|
this.tokenNameToTypeMap = ComputeTokenTypes( tokenNames );
|
|
}
|
|
|
|
public TreeWizard( string[] tokenNames )
|
|
: this( new CommonTreeAdaptor(), tokenNames )
|
|
{
|
|
}
|
|
|
|
/** <summary>
|
|
* Compute a Map<String, Integer> that is an inverted index of
|
|
* tokenNames (which maps int token types to names).
|
|
* </summary>
|
|
*/
|
|
public virtual IDictionary<string, int> ComputeTokenTypes( string[] tokenNames )
|
|
{
|
|
IDictionary<string, int> m = new Dictionary<string, int>();
|
|
if ( tokenNames == null )
|
|
{
|
|
return m;
|
|
}
|
|
for ( int ttype = TokenTypes.Min; ttype < tokenNames.Length; ttype++ )
|
|
{
|
|
string name = tokenNames[ttype];
|
|
m[name] = ttype;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
/** <summary>Using the map of token names to token types, return the type.</summary> */
|
|
public virtual int GetTokenType( string tokenName )
|
|
{
|
|
if ( tokenNameToTypeMap == null )
|
|
{
|
|
return TokenTypes.Invalid;
|
|
}
|
|
|
|
int value;
|
|
if ( tokenNameToTypeMap.TryGetValue( tokenName, out value ) )
|
|
return value;
|
|
|
|
return TokenTypes.Invalid;
|
|
}
|
|
|
|
/** <summary>
|
|
* Walk the entire tree and make a node name to nodes mapping.
|
|
* For now, use recursion but later nonrecursive version may be
|
|
* more efficient. Returns Map<Integer, List> where the List is
|
|
* of your AST node type. The Integer is the token type of the node.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* TODO: save this index so that find and visit are faster
|
|
* </remarks>
|
|
*/
|
|
public IDictionary<int, IList> Index( object t )
|
|
{
|
|
IDictionary<int, IList> m = new Dictionary<int, IList>();
|
|
IndexCore( t, m );
|
|
return m;
|
|
}
|
|
|
|
/** <summary>Do the work for index</summary> */
|
|
protected virtual void IndexCore( object t, IDictionary<int, IList> m )
|
|
{
|
|
if ( t == null )
|
|
{
|
|
return;
|
|
}
|
|
int ttype = adaptor.GetType( t );
|
|
IList elements;
|
|
if ( !m.TryGetValue( ttype, out elements ) || elements == null )
|
|
{
|
|
elements = new List<object>();
|
|
m[ttype] = elements;
|
|
}
|
|
elements.Add( t );
|
|
int n = adaptor.GetChildCount( t );
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
object child = adaptor.GetChild( t, i );
|
|
IndexCore( child, m );
|
|
}
|
|
}
|
|
|
|
class FindTreeWizardVisitor : TreeWizard.Visitor
|
|
{
|
|
IList _nodes;
|
|
public FindTreeWizardVisitor( IList nodes )
|
|
{
|
|
_nodes = nodes;
|
|
}
|
|
public override void Visit( object t )
|
|
{
|
|
_nodes.Add( t );
|
|
}
|
|
}
|
|
class FindTreeWizardContextVisitor : TreeWizard.IContextVisitor
|
|
{
|
|
TreeWizard _outer;
|
|
TreePattern _tpattern;
|
|
IList _subtrees;
|
|
public FindTreeWizardContextVisitor( TreeWizard outer, TreePattern tpattern, IList subtrees )
|
|
{
|
|
_outer = outer;
|
|
_tpattern = tpattern;
|
|
_subtrees = subtrees;
|
|
}
|
|
|
|
public void Visit( object t, object parent, int childIndex, IDictionary<string, object> labels )
|
|
{
|
|
if ( _outer.ParseCore( t, _tpattern, null ) )
|
|
{
|
|
_subtrees.Add( t );
|
|
}
|
|
}
|
|
}
|
|
|
|
/** <summary>Return a List of tree nodes with token type ttype</summary> */
|
|
public virtual IList Find( object t, int ttype )
|
|
{
|
|
IList nodes = new List<object>();
|
|
Visit( t, ttype, new FindTreeWizardVisitor( nodes ) );
|
|
return nodes;
|
|
}
|
|
|
|
/** <summary>Return a List of subtrees matching pattern.</summary> */
|
|
public virtual IList Find( object t, string pattern )
|
|
{
|
|
IList subtrees = new List<object>();
|
|
// Create a TreePattern from the pattern
|
|
TreePatternLexer tokenizer = new TreePatternLexer( pattern );
|
|
TreePatternParser parser =
|
|
new TreePatternParser( tokenizer, this, new TreePatternTreeAdaptor() );
|
|
TreePattern tpattern = (TreePattern)parser.Pattern();
|
|
// don't allow invalid patterns
|
|
if ( tpattern == null ||
|
|
tpattern.IsNil ||
|
|
tpattern.GetType() == typeof( WildcardTreePattern ) )
|
|
{
|
|
return null;
|
|
}
|
|
int rootTokenType = tpattern.Type;
|
|
Visit( t, rootTokenType, new FindTreeWizardContextVisitor( this, tpattern, subtrees ) );
|
|
return subtrees;
|
|
}
|
|
|
|
public virtual object FindFirst( object t, int ttype )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public virtual object FindFirst( object t, string pattern )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/** <summary>
|
|
* Visit every ttype node in t, invoking the visitor. This is a quicker
|
|
* version of the general visit(t, pattern) method. The labels arg
|
|
* of the visitor action method is never set (it's null) since using
|
|
* a token type rather than a pattern doesn't let us set a label.
|
|
* </summary>
|
|
*/
|
|
public void Visit( object t, int ttype, IContextVisitor visitor )
|
|
{
|
|
VisitCore( t, null, 0, ttype, visitor );
|
|
}
|
|
|
|
public void Visit( object t, int ttype, System.Action<object> action )
|
|
{
|
|
Visit( t, ttype, new ActionVisitor( action ) );
|
|
}
|
|
|
|
/** <summary>Do the recursive work for visit</summary> */
|
|
protected virtual void VisitCore( object t, object parent, int childIndex, int ttype, IContextVisitor visitor )
|
|
{
|
|
if ( t == null )
|
|
{
|
|
return;
|
|
}
|
|
if ( adaptor.GetType( t ) == ttype )
|
|
{
|
|
visitor.Visit( t, parent, childIndex, null );
|
|
}
|
|
int n = adaptor.GetChildCount( t );
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
object child = adaptor.GetChild( t, i );
|
|
VisitCore( child, t, i, ttype, visitor );
|
|
}
|
|
}
|
|
|
|
class VisitTreeWizardContextVisitor : TreeWizard.IContextVisitor
|
|
{
|
|
TreeWizard _outer;
|
|
IContextVisitor _visitor;
|
|
IDictionary<string, object> _labels;
|
|
TreePattern _tpattern;
|
|
|
|
public VisitTreeWizardContextVisitor( TreeWizard outer, IContextVisitor visitor, IDictionary<string, object> labels, TreePattern tpattern )
|
|
{
|
|
_outer = outer;
|
|
_visitor = visitor;
|
|
_labels = labels;
|
|
_tpattern = tpattern;
|
|
}
|
|
|
|
public void Visit( object t, object parent, int childIndex, IDictionary<string, object> unusedlabels )
|
|
{
|
|
// the unusedlabels arg is null as visit on token type doesn't set.
|
|
_labels.Clear();
|
|
if ( _outer.ParseCore( t, _tpattern, _labels ) )
|
|
{
|
|
_visitor.Visit( t, parent, childIndex, _labels );
|
|
}
|
|
}
|
|
}
|
|
|
|
/** <summary>
|
|
* For all subtrees that match the pattern, execute the visit action.
|
|
* The implementation uses the root node of the pattern in combination
|
|
* with visit(t, ttype, visitor) so nil-rooted patterns are not allowed.
|
|
* Patterns with wildcard roots are also not allowed.
|
|
* </summary>
|
|
*/
|
|
public void Visit( object t, string pattern, IContextVisitor visitor )
|
|
{
|
|
// Create a TreePattern from the pattern
|
|
TreePatternLexer tokenizer = new TreePatternLexer( pattern );
|
|
TreePatternParser parser =
|
|
new TreePatternParser( tokenizer, this, new TreePatternTreeAdaptor() );
|
|
TreePattern tpattern = (TreePattern)parser.Pattern();
|
|
// don't allow invalid patterns
|
|
if ( tpattern == null ||
|
|
tpattern.IsNil ||
|
|
tpattern.GetType() == typeof( WildcardTreePattern ) )
|
|
{
|
|
return;
|
|
}
|
|
IDictionary<string, object> labels = new Dictionary<string, object>(); // reused for each _parse
|
|
int rootTokenType = tpattern.Type;
|
|
Visit( t, rootTokenType, new VisitTreeWizardContextVisitor( this, visitor, labels, tpattern ) );
|
|
}
|
|
|
|
/** <summary>
|
|
* Given a pattern like (ASSIGN %lhs:ID %rhs:.) with optional labels
|
|
* on the various nodes and '.' (dot) as the node/subtree wildcard,
|
|
* return true if the pattern matches and fill the labels Map with
|
|
* the labels pointing at the appropriate nodes. Return false if
|
|
* the pattern is malformed or the tree does not match.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* If a node specifies a text arg in pattern, then that must match
|
|
* for that node in t.
|
|
*
|
|
* TODO: what's a better way to indicate bad pattern? Exceptions are a hassle
|
|
* </remarks>
|
|
*/
|
|
public bool Parse( object t, string pattern, IDictionary<string, object> labels )
|
|
{
|
|
TreePatternLexer tokenizer = new TreePatternLexer( pattern );
|
|
TreePatternParser parser =
|
|
new TreePatternParser( tokenizer, this, new TreePatternTreeAdaptor() );
|
|
TreePattern tpattern = (TreePattern)parser.Pattern();
|
|
/*
|
|
System.out.println("t="+((Tree)t).toStringTree());
|
|
System.out.println("scant="+tpattern.toStringTree());
|
|
*/
|
|
bool matched = ParseCore( t, tpattern, labels );
|
|
return matched;
|
|
}
|
|
|
|
public bool Parse( object t, string pattern )
|
|
{
|
|
return Parse( t, pattern, null );
|
|
}
|
|
|
|
/** <summary>
|
|
* Do the work for parse. Check to see if the t2 pattern fits the
|
|
* structure and token types in t1. Check text if the pattern has
|
|
* text arguments on nodes. Fill labels map with pointers to nodes
|
|
* in tree matched against nodes in pattern with labels.
|
|
* </summary>
|
|
*/
|
|
protected virtual bool ParseCore( object t1, TreePattern tpattern, IDictionary<string, object> labels )
|
|
{
|
|
// make sure both are non-null
|
|
if ( t1 == null || tpattern == null )
|
|
{
|
|
return false;
|
|
}
|
|
// check roots (wildcard matches anything)
|
|
if ( tpattern.GetType() != typeof( WildcardTreePattern ) )
|
|
{
|
|
if ( adaptor.GetType( t1 ) != tpattern.Type )
|
|
{
|
|
return false;
|
|
}
|
|
// if pattern has text, check node text
|
|
if ( tpattern.hasTextArg && !adaptor.GetText( t1 ).Equals( tpattern.Text ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if ( tpattern.label != null && labels != null )
|
|
{
|
|
// map label in pattern to node in t1
|
|
labels[tpattern.label] = t1;
|
|
}
|
|
// check children
|
|
int n1 = adaptor.GetChildCount( t1 );
|
|
int n2 = tpattern.ChildCount;
|
|
if ( n1 != n2 )
|
|
{
|
|
return false;
|
|
}
|
|
for ( int i = 0; i < n1; i++ )
|
|
{
|
|
object child1 = adaptor.GetChild( t1, i );
|
|
TreePattern child2 = (TreePattern)tpattern.GetChild( i );
|
|
if ( !ParseCore( child1, child2, labels ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** <summary>
|
|
* Create a tree or node from the indicated tree pattern that closely
|
|
* follows ANTLR tree grammar tree element syntax:
|
|
*
|
|
* (root child1 ... child2).
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* You can also just pass in a node: ID
|
|
*
|
|
* Any node can have a text argument: ID[foo]
|
|
* (notice there are no quotes around foo--it's clear it's a string).
|
|
*
|
|
* nil is a special name meaning "give me a nil node". Useful for
|
|
* making lists: (nil A B C) is a list of A B C.
|
|
* </remarks>
|
|
*/
|
|
public virtual object Create( string pattern )
|
|
{
|
|
TreePatternLexer tokenizer = new TreePatternLexer( pattern );
|
|
TreePatternParser parser = new TreePatternParser( tokenizer, this, adaptor );
|
|
object t = parser.Pattern();
|
|
return t;
|
|
}
|
|
|
|
/** <summary>
|
|
* Compare t1 and t2; return true if token types/text, structure match exactly.
|
|
* The trees are examined in their entirety so that (A B) does not match
|
|
* (A B C) nor (A (B C)).
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* TODO: allow them to pass in a comparator
|
|
* TODO: have a version that is nonstatic so it can use instance adaptor
|
|
*
|
|
* I cannot rely on the tree node's equals() implementation as I make
|
|
* no constraints at all on the node types nor interface etc...
|
|
* </remarks>
|
|
*/
|
|
public static bool Equals( object t1, object t2, ITreeAdaptor adaptor )
|
|
{
|
|
return EqualsCore( t1, t2, adaptor );
|
|
}
|
|
|
|
/** <summary>
|
|
* Compare type, structure, and text of two trees, assuming adaptor in
|
|
* this instance of a TreeWizard.
|
|
* </summary>
|
|
*/
|
|
public new bool Equals( object t1, object t2 )
|
|
{
|
|
return EqualsCore( t1, t2, adaptor );
|
|
}
|
|
|
|
protected static bool EqualsCore( object t1, object t2, ITreeAdaptor adaptor )
|
|
{
|
|
// make sure both are non-null
|
|
if ( t1 == null || t2 == null )
|
|
{
|
|
return false;
|
|
}
|
|
// check roots
|
|
if ( adaptor.GetType( t1 ) != adaptor.GetType( t2 ) )
|
|
{
|
|
return false;
|
|
}
|
|
if ( !adaptor.GetText( t1 ).Equals( adaptor.GetText( t2 ) ) )
|
|
{
|
|
return false;
|
|
}
|
|
// check children
|
|
int n1 = adaptor.GetChildCount( t1 );
|
|
int n2 = adaptor.GetChildCount( t2 );
|
|
if ( n1 != n2 )
|
|
{
|
|
return false;
|
|
}
|
|
for ( int i = 0; i < n1; i++ )
|
|
{
|
|
object child1 = adaptor.GetChild( t1, i );
|
|
object child2 = adaptor.GetChild( t2, i );
|
|
if ( !EqualsCore( child1, child2, adaptor ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if BUILD_INDEXES
|
|
// TODO: next stuff taken from CommonTreeNodeStream
|
|
|
|
/** <summary>
|
|
* Given a node, add this to the reverse index tokenTypeToStreamIndexesMap.
|
|
* You can override this method to alter how indexing occurs. The
|
|
* default is to create a
|
|
*
|
|
* Map<Integer token type,ArrayList<Integer stream index>>
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* This data structure allows you to find all nodes with type INT in order.
|
|
*
|
|
* If you really need to find a node of type, say, FUNC quickly then perhaps
|
|
*
|
|
* Map<Integertoken type,Map<Object tree node,Integer stream index>>
|
|
*
|
|
* would be better for you. The interior maps map a tree node to
|
|
* the index so you don't have to search linearly for a specific node.
|
|
*
|
|
* If you change this method, you will likely need to change
|
|
* getNodeIndex(), which extracts information.
|
|
* </remarks>
|
|
*/
|
|
protected void fillReverseIndex( object node, int streamIndex )
|
|
{
|
|
//System.out.println("revIndex "+node+"@"+streamIndex);
|
|
if ( tokenTypesToReverseIndex == null )
|
|
{
|
|
return; // no indexing if this is empty (nothing of interest)
|
|
}
|
|
if ( tokenTypeToStreamIndexesMap == null )
|
|
{
|
|
tokenTypeToStreamIndexesMap = new Dictionary<int, IList<int>>(); // first indexing op
|
|
}
|
|
int tokenType = adaptor.getType( node );
|
|
if ( !( tokenTypesToReverseIndex == INDEX_ALL ||
|
|
tokenTypesToReverseIndex.Contains( tokenType ) ) )
|
|
{
|
|
return; // tokenType not of interest
|
|
}
|
|
IList<int> indexes;
|
|
|
|
if ( !tokenTypeToStreamIndexesMap.TryGetValue( tokenType, out indexes ) || indexes == null )
|
|
{
|
|
indexes = new List<int>(); // no list yet for this token type
|
|
indexes.Add( streamIndex ); // not there yet, add
|
|
tokenTypeToStreamIndexesMap[tokenType] = indexes;
|
|
}
|
|
else
|
|
{
|
|
if ( !indexes.Contains( streamIndex ) )
|
|
{
|
|
indexes.Add( streamIndex ); // not there yet, add
|
|
}
|
|
}
|
|
}
|
|
|
|
/** <summary>
|
|
* Track the indicated token type in the reverse index. Call this
|
|
* repeatedly for each type or use variant with Set argument to
|
|
* set all at once.
|
|
* </summary>
|
|
*
|
|
* <param name="tokenType" />
|
|
*/
|
|
public void reverseIndex( int tokenType )
|
|
{
|
|
if ( tokenTypesToReverseIndex == null )
|
|
{
|
|
tokenTypesToReverseIndex = new HashSet<int>();
|
|
}
|
|
else if ( tokenTypesToReverseIndex == INDEX_ALL )
|
|
{
|
|
return;
|
|
}
|
|
tokenTypesToReverseIndex.add( tokenType );
|
|
}
|
|
|
|
/** <summary>
|
|
* Track the indicated token types in the reverse index. Set
|
|
* to INDEX_ALL to track all token types.
|
|
* </summary>
|
|
*/
|
|
public void reverseIndex( HashSet<int> tokenTypes )
|
|
{
|
|
tokenTypesToReverseIndex = tokenTypes;
|
|
}
|
|
|
|
/** <summary>
|
|
* Given a node pointer, return its index into the node stream.
|
|
* This is not its Token stream index. If there is no reverse map
|
|
* from node to stream index or the map does not contain entries
|
|
* for node's token type, a linear search of entire stream is used.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* Return -1 if exact node pointer not in stream.
|
|
* </remarks>
|
|
*/
|
|
public int getNodeIndex( object node )
|
|
{
|
|
//System.out.println("get "+node);
|
|
if ( tokenTypeToStreamIndexesMap == null )
|
|
{
|
|
return getNodeIndexLinearly( node );
|
|
}
|
|
int tokenType = adaptor.getType( node );
|
|
IList<int> indexes;
|
|
if ( !tokenTypeToStreamIndexesMap.TryGetValue( tokenType, out indexes ) || indexes == null )
|
|
{
|
|
//System.out.println("found linearly; stream index = "+getNodeIndexLinearly(node));
|
|
return getNodeIndexLinearly( node );
|
|
}
|
|
for ( int i = 0; i < indexes.size(); i++ )
|
|
{
|
|
int streamIndex = indexes[i];
|
|
object n = get( streamIndex );
|
|
if ( n == node )
|
|
{
|
|
//System.out.println("found in index; stream index = "+streamIndexI);
|
|
return streamIndex; // found it!
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|