blob: 5fe15f1dd525b61fa7a0be2cac87ff0dc8d075b9 [file] [log] [blame]
/*
* [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(null, tokenNames) {
}
/** <summary>
* Compute a Map&lt;String, Integer&gt; 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&lt;Integer, List&gt; 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&lt;Integer token type,ArrayList&lt;Integer stream index&gt;&gt;
* </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&lt;Integertoken type,Map&lt;Object tree node,Integer stream index&gt;&gt;
*
* 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
}
}