ANTLRTreeWizard.m revision 324c4644fee44b9898524c09511bd33c3f12e2df
1//
2//  ANTLRTreeWizard.m
3//  ANTLR
4//
5//  Created by Alan Condit on 6/18/10.
6// [The "BSD licence"]
7// Copyright (c) 2010 Alan Condit
8// All rights reserved.
9//
10// Redistribution and use in source and binary forms, with or without
11// modification, are permitted provided that the following conditions
12// are met:
13// 1. Redistributions of source code must retain the above copyright
14//    notice, this list of conditions and the following disclaimer.
15// 2. Redistributions in binary form must reproduce the above copyright
16//    notice, this list of conditions and the following disclaimer in the
17//    documentation and/or other materials provided with the distribution.
18// 3. The name of the author may not be used to endorse or promote products
19//    derived from this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32#import "ANTLRTreeWizard.h"
33#import "ANTLRTreePatternLexer.h"
34#import "ANTLRTreePatternParser.h"
35#import "ANTLRIntArray.h"
36
37@implementation ANTLRVisitor
38
39+ (ANTLRVisitor *)newANTLRVisitor:(NSInteger)anAction Actor:(id)anActor Object:(id)anObject1 Object:(id)anObject2
40{
41    return [[ANTLRVisitor alloc] initWithAction:anAction Actor:(id)anActor Object:(id)anObject1 Object:(id)anObject2];
42}
43
44- (id) initWithAction:(NSInteger)anAction Actor:(id)anActor Object:(id)anObject1 Object:(id)anObject2
45{
46    if ((self = [super init]) != nil) {
47        action = anAction;
48        actor = anActor;
49        if ( actor ) [actor retain];
50        object1 = anObject1;
51        if ( object1 ) [object1 retain];
52        object2 = anObject2;
53        if ( object2 ) [object2 retain];
54    }
55    return self;
56}
57
58- (void) dealloc
59{
60#ifdef DEBUG_DEALLOC
61    NSLog( @"called dealloc in ANTLRVisitor" );
62#endif
63    if ( actor ) [actor release];
64    if ( object1 ) [object1 release];
65    if ( object2 ) [object2 release];
66    [super dealloc];
67}
68
69- (void) visit:(ANTLRCommonTree *)t Parent:(ANTLRCommonTree *)parent ChildIndex:(NSInteger)childIndex Map:(ANTLRMap *)labels
70{
71    switch (action) {
72        case 0:
73            [(ANTLRMap *)object2 /* labels */ clear];
74            if ( [(ANTLRTreeWizard *)actor _parse:t Pattern:object1/* tpattern */ Map:object2 /* labels */] ) {
75                [self visit:t Parent:parent ChildIndex:childIndex Map:object2 /* labels */];
76            }
77            break;
78        case 1:
79            if ( [(ANTLRTreeWizard *)actor _parse:t Pattern:object1/* tpattern */ Map:nil] ) {
80                [(AMutableArray *)object2/* subtrees */ addObject:t];
81            }
82            break;
83    }
84    // [self visit:t];
85    return;
86}
87
88- (void) visit:(ANTLRCommonTree *)t
89{
90    [object1 addObject:t];
91    return;
92}
93
94@synthesize action;
95@synthesize actor;
96@synthesize object1;
97@synthesize object2;
98@end
99
100/** When using %label:TOKENNAME in a tree for parse(), we must
101 *  track the label.
102 */
103@implementation ANTLRTreePattern
104
105@synthesize label;
106@synthesize hasTextArg;
107
108+ (ANTLRCommonTree *)newANTLRTreePattern:(id<ANTLRToken>)payload
109{
110    return (ANTLRCommonTree *)[[ANTLRTreePattern alloc] initWithToken:payload];
111}
112
113- (id) initWithToken:(id<ANTLRToken>)payload
114{
115    self = [super initWithToken:payload];
116    if ( self != nil ) {
117    }
118    return (ANTLRCommonTree *)self;
119}
120
121- (void) dealloc
122{
123#ifdef DEBUG_DEALLOC
124    NSLog( @"called dealloc in ANTLRTreePattern" );
125#endif
126    if ( label ) [label release];
127    [super dealloc];
128}
129
130- (NSString *)toString
131{
132    if ( label != nil ) {
133        return [NSString stringWithFormat:@"\% %@ : %@", label, [super toString]];
134    }
135    else {
136        return [super toString];				
137    }
138}
139
140@end
141
142@implementation ANTLRWildcardTreePattern
143
144+ (ANTLRWildcardTreePattern *)newANTLRWildcardTreePattern:(id<ANTLRToken>)payload
145{
146    return(ANTLRWildcardTreePattern *)[[ANTLRWildcardTreePattern alloc] initWithToken:(id<ANTLRToken>)payload];
147}
148
149- (id) initWithToken:(id<ANTLRToken>)payload
150{
151    self = [super initWithToken:payload];
152    if ( self != nil ) {
153    }
154    return self;
155}
156
157@end
158
159/** This adaptor creates TreePattern objects for use during scan() */
160@implementation ANTLRTreePatternTreeAdaptor
161
162+ (ANTLRTreePatternTreeAdaptor *)newTreeAdaptor
163{
164    return [[ANTLRTreePatternTreeAdaptor alloc] init];
165}
166
167- (id) init
168{
169    self = [super init];
170    if ( self != nil ) {
171    }
172    return self;
173}
174
175- (ANTLRCommonTree *)createTreePattern:(id<ANTLRToken>)payload
176{
177    return (ANTLRCommonTree *)[super create:payload];
178}
179          
180@end
181
182@implementation ANTLRTreeWizard
183
184// TODO: build indexes for the wizard
185
186/** During fillBuffer(), we can make a reverse index from a set
187 *  of token types of interest to the list of indexes into the
188 *  node stream.  This lets us convert a node pointer to a
189 *  stream index semi-efficiently for a list of interesting
190 *  nodes such as function definition nodes (you'll want to seek
191 *  to their bodies for an interpreter).  Also useful for doing
192 *  dynamic searches; i.e., go find me all PLUS nodes.
193 protected Map tokenTypeToStreamIndexesMap;
194 
195 ** If tokenTypesToReverseIndex set to INDEX_ALL then indexing
196 *  occurs for all token types.
197 public static final Set INDEX_ALL = new HashSet();
198 
199 ** A set of token types user would like to index for faster lookup.
200 *  If this is INDEX_ALL, then all token types are tracked.  If nil,
201 *  then none are indexed.
202 protected Set tokenTypesToReverseIndex = nil;
203 */
204
205+ (ANTLRTreeWizard *) newANTLRTreeWizard:(id<ANTLRTreeAdaptor>)anAdaptor
206{
207    return [[ANTLRTreeWizard alloc] initWithAdaptor:anAdaptor];
208}
209
210+ (ANTLRTreeWizard *)newANTLRTreeWizard:(id<ANTLRTreeAdaptor>)anAdaptor Map:(ANTLRMap *)aTokenNameToTypeMap
211{
212    return [[ANTLRTreeWizard alloc] initWithAdaptor:anAdaptor Map:aTokenNameToTypeMap];
213}
214
215+ (ANTLRTreeWizard *)newANTLRTreeWizard:(id<ANTLRTreeAdaptor>)anAdaptor TokenNames:(NSArray *)theTokNams
216{
217    return [[ANTLRTreeWizard alloc] initWithTokenNames:anAdaptor TokenNames:theTokNams];
218}
219
220+ (ANTLRTreeWizard *)newANTLRTreeWizardWithTokenNames:(NSArray *)theTokNams
221{
222    return [[ANTLRTreeWizard alloc] initWithTokenNames:theTokNams];
223}
224
225- (id) init
226{
227    if ((self = [super init]) != nil) {
228    }
229    return self;
230}
231
232- (id) initWithAdaptor:(id<ANTLRTreeAdaptor>)anAdaptor
233{
234    if ((self = [super init]) != nil) {
235        adaptor = anAdaptor;
236        if ( adaptor ) [adaptor retain];
237    }
238    return self;
239}
240            
241- (id) initWithAdaptor:(id<ANTLRTreeAdaptor>)anAdaptor Map:(ANTLRMap *)aTokenNameToTypeMap
242{
243    if ((self = [super init]) != nil) {
244        adaptor = anAdaptor;
245        if ( adaptor ) [adaptor retain];
246        tokenNameToTypeMap = aTokenNameToTypeMap;
247   }
248    return self;
249}
250
251- (id) initWithTokenNames:(NSArray *)theTokNams
252{
253    if ((self = [super init]) != nil) {
254#pragma warning Fix initWithTokenNames.
255        // adaptor = anAdaptor;
256        //tokenNameToTypeMap = aTokenNameToTypeMap;
257        tokenNameToTypeMap = [[self computeTokenTypes:theTokNams] retain];
258    }
259    return self;
260}
261             
262- (id) initWithTokenNames:(id<ANTLRTreeAdaptor>)anAdaptor TokenNames:(NSArray *)theTokNams
263{
264    if ((self = [super init]) != nil) {
265        adaptor = anAdaptor;
266        if ( adaptor ) [adaptor retain];
267        // tokenNameToTypeMap = aTokenNameToTypeMap;
268        tokenNameToTypeMap = [[self computeTokenTypes:theTokNams] retain];
269    }
270    return self;
271}
272            
273- (void) dealloc
274{
275#ifdef DEBUG_DEALLOC
276    NSLog( @"called dealloc in ANTLRTreePatternTreeAdaptor" );
277#endif
278    if ( adaptor ) [adaptor release];
279    if ( tokenNameToTypeMap ) [tokenNameToTypeMap release];
280    [super dealloc];
281}
282
283/** Compute a Map<String, Integer> that is an inverted index of
284 *  tokenNames (which maps int token types to names).
285 */
286- (ANTLRMap *)computeTokenTypes:(NSArray *)theTokNams
287{
288    ANTLRMap *m = [ANTLRMap newANTLRMap];
289    if ( theTokNams == nil ) {
290        return m;
291    }
292    for (int ttype = ANTLRTokenTypeMIN; ttype < [theTokNams count]; ttype++) {
293        NSString *name = (NSString *) [theTokNams objectAtIndex:ttype];
294        [m putName:name TType:ttype];
295    }
296    return m;
297}
298
299/** Using the map of token names to token types, return the type. */
300- (NSInteger)getTokenType:(NSString *)tokenName
301{
302    if ( tokenNameToTypeMap == nil ) {
303        return ANTLRTokenTypeInvalid;
304    }
305    NSInteger aTType = (NSInteger)[tokenNameToTypeMap getTType:tokenName];
306    if ( aTType != -1 ) {
307        return aTType;
308    }
309    return ANTLRTokenTypeInvalid;
310}
311
312/** Walk the entire tree and make a node name to nodes mapping.
313 *  For now, use recursion but later nonrecursive version may be
314 *  more efficient.  Returns Map<Integer, List> where the List is
315 *  of your AST node type.  The Integer is the token type of the node.
316 *
317 *  TODO: save this index so that find and visit are faster
318 */
319- (ANTLRMap *)index:(ANTLRCommonTree *)t
320{
321    ANTLRMap *m = [ANTLRMap newANTLRMap];
322    [self _index:t Map:m];
323    return m;
324}
325
326/** Do the work for index */
327- (void) _index:(ANTLRCommonTree *)t Map:(ANTLRMap *)m
328{
329    if ( t==nil ) {
330        return;
331    }
332#pragma warning Fix _index use of ANTLRMap.
333    NSInteger ttype = [adaptor getType:t];
334    ANTLRMap *elements = (ANTLRMap *)[m getName:ttype];
335    if ( elements == nil ) {
336        elements = [ANTLRMap newANTLRMapWithLen:100];
337        [m putNode:ttype Node:elements];
338    }
339    [elements addObject:t];
340    int n = [adaptor getChildCount:t];
341    for (int i=0; i<n; i++) {
342        ANTLRCommonTree * child = [adaptor getChild:t At:i];
343        [self _index:child Map:m];
344    }
345}
346
347/** Return a List of tree nodes with token type ttype */
348- (AMutableArray *)find:(ANTLRCommonTree *)t Type:(NSInteger)ttype
349{
350#ifdef DONTUSENOMO
351    final List nodes = new ArrayList();
352    visit(t, ttype, new TreeWizard.Visitor() {
353        public void visit(Object t) {
354            [nodes addObject t];
355        }
356    } );
357#endif
358    AMutableArray *nodes = [AMutableArray arrayWithCapacity:100];
359    ANTLRVisitor *contextVisitor = [ANTLRVisitor newANTLRVisitor:3 Actor:self Object:(id)nodes Object:nil];
360    [self visit:t Type:ttype Visitor:contextVisitor];
361    return nodes;
362}
363
364/** Return a List of subtrees matching pattern. */
365- (AMutableArray *)find:(ANTLRCommonTree *)t Pattern:(NSString *)pattern
366{
367    AMutableArray *subtrees = [AMutableArray arrayWithCapacity:100];
368    // Create a TreePattern from the pattern
369    ANTLRTreePatternLexer *tokenizer = [ANTLRTreePatternLexer newANTLRTreePatternLexer:pattern];
370    ANTLRTreePatternParser *parser = [ANTLRTreePatternParser newANTLRTreePatternParser:tokenizer
371                                                                                     Wizard:self
372                                                                                    Adaptor:[ANTLRTreePatternTreeAdaptor newTreeAdaptor]];
373    ANTLRCommonTree *tpattern = (ANTLRCommonTree *)[parser pattern];
374    // don't allow invalid patterns
375    if ( tpattern == nil ||
376        [tpattern isNil] ||
377        [tpattern class] == [ANTLRWildcardTreePattern class] )
378    {
379        return nil;
380    }
381    int rootTokenType = [tpattern type];
382#ifdef DONTUSENOMO
383    visit(t, rootTokenType, new TreeWizard.ContextVisitor() {
384        public void visit(Object t, Object parent, int childIndex, Map labels) {
385            if ( _parse(t, tpattern, null) ) {
386                subtrees.add(t);
387            }
388        }
389    } );
390#endif
391    ANTLRVisitor *contextVisitor = [ANTLRVisitor newANTLRVisitor:1 Actor:self Object:tpattern Object:subtrees];
392    [self visit:t Type:rootTokenType Visitor:contextVisitor];
393    return subtrees;
394}
395
396- (ANTLRTreeWizard *)findFirst:(ANTLRCommonTree *) t Type:(NSInteger)ttype
397{
398    return nil;
399}
400
401- (ANTLRTreeWizard *)findFirst:(ANTLRCommonTree *) t Pattern:(NSString *)pattern
402{
403    return nil;
404}
405
406/** Visit every ttype node in t, invoking the visitor.  This is a quicker
407 *  version of the general visit(t, pattern) method.  The labels arg
408 *  of the visitor action method is never set (it's nil) since using
409 *  a token type rather than a pattern doesn't let us set a label.
410 */
411- (void) visit:(ANTLRCommonTree *)t Type:(NSInteger)ttype Visitor:(ANTLRVisitor *)visitor
412{
413    [self _visit:t Parent:nil ChildIndex:0 Type:ttype Visitor:visitor];
414}
415
416/** Do the recursive work for visit */
417- (void) _visit:(ANTLRCommonTree *)t
418         Parent:(ANTLRCommonTree *)parent
419     ChildIndex:(NSInteger)childIndex
420           Type:(NSInteger)ttype
421        Visitor:(ANTLRVisitor *)visitor
422{
423    if ( t == nil ) {
424        return;
425    }
426    if ( [adaptor getType:t] == ttype ) {
427        [visitor visit:t Parent:parent ChildIndex:childIndex Map:nil];
428    }
429    int n = [adaptor getChildCount:t];
430    for (int i=0; i<n; i++) {
431        ANTLRCommonTree * child = [adaptor getChild:t At:i];
432        [self _visit:child Parent:t ChildIndex:i Type:ttype Visitor:visitor];
433    }
434}
435
436/** For all subtrees that match the pattern, execute the visit action.
437 *  The implementation uses the root node of the pattern in combination
438 *  with visit(t, ttype, visitor) so nil-rooted patterns are not allowed.
439 *  Patterns with wildcard roots are also not allowed.
440 */
441- (void)visit:(ANTLRCommonTree *)t Pattern:(NSString *)pattern Visitor:(ANTLRVisitor *)visitor
442{
443    // Create a TreePattern from the pattern
444    ANTLRTreePatternLexer *tokenizer = [ANTLRTreePatternLexer newANTLRTreePatternLexer:pattern];
445    ANTLRTreePatternParser *parser =
446    [ANTLRTreePatternParser newANTLRTreePatternParser:tokenizer Wizard:self Adaptor:[ANTLRTreePatternTreeAdaptor newTreeAdaptor]];
447    ANTLRCommonTree *tpattern = [parser pattern];
448    // don't allow invalid patterns
449    if ( tpattern == nil ||
450        [tpattern isNil] ||
451        [tpattern class] == [ANTLRWildcardTreePattern class] )
452    {
453        return;
454    }
455    ANTLRMapElement *labels = [ANTLRMap newANTLRMap]; // reused for each _parse
456    int rootTokenType = [tpattern type];
457#pragma warning This is another one of those screwy nested constructs that I have to figure out
458#ifdef DONTUSENOMO
459    visit(t, rootTokenType, new TreeWizard.ContextVisitor() {
460        public void visit(Object t, Object parent, int childIndex, Map unusedlabels) {
461            // the unusedlabels arg is null as visit on token type doesn't set.
462            labels.clear();
463            if ( _parse(t, tpattern, labels) ) {
464                visitor.visit(t, parent, childIndex, labels);
465            }
466        }
467    });
468#endif
469    ANTLRVisitor *contextVisitor = [ANTLRVisitor newANTLRVisitor:0 Actor:self Object:tpattern Object:labels];
470    [self visit:t Type:rootTokenType Visitor:contextVisitor];
471}
472
473/** Given a pattern like (ASSIGN %lhs:ID %rhs:.) with optional labels
474 *  on the various nodes and '.' (dot) as the node/subtree wildcard,
475 *  return true if the pattern matches and fill the labels Map with
476 *  the labels pointing at the appropriate nodes.  Return false if
477 *  the pattern is malformed or the tree does not match.
478 *
479 *  If a node specifies a text arg in pattern, then that must match
480 *  for that node in t.
481 *
482 *  TODO: what's a better way to indicate bad pattern? Exceptions are a hassle 
483 */
484- (BOOL)parse:(ANTLRCommonTree *)t Pattern:(NSString *)pattern Map:(ANTLRMap *)labels
485{
486#ifdef DONTUSENOMO
487    TreePatternLexer tokenizer = new TreePatternLexer(pattern);
488    TreePatternParser parser =
489    new TreePatternParser(tokenizer, this, new TreePatternTreeAdaptor());
490    TreePattern tpattern = (TreePattern)parser.pattern();
491    /*
492     System.out.println("t="+((Tree)t).toStringTree());
493     System.out.println("scant="+tpattern.toStringTree());
494     */
495    boolean matched = _parse(t, tpattern, labels);
496    return matched;
497#endif
498    ANTLRTreePatternLexer *tokenizer = [ANTLRTreePatternLexer newANTLRTreePatternLexer:pattern];
499    ANTLRTreePatternParser *parser = [ANTLRTreePatternParser newANTLRTreePatternParser:tokenizer
500                                                                                Wizard:self
501                                                                               Adaptor:[ANTLRTreePatternTreeAdaptor newTreeAdaptor]];
502    ANTLRCommonTree *tpattern = [parser pattern];
503    /*
504     System.out.println("t="+((Tree)t).toStringTree());
505     System.out.println("scant="+tpattern.toStringTree());
506     */
507    //BOOL matched = [self _parse:t Pattern:tpattern Map:labels];
508    //return matched;
509    return [self _parse:t Pattern:tpattern Map:labels];
510}
511
512- (BOOL) parse:(ANTLRCommonTree *)t Pattern:(NSString *)pattern
513{
514    return [self parse:t Pattern:pattern Map:nil];
515}
516
517/** Do the work for parse. Check to see if the t2 pattern fits the
518 *  structure and token types in t1.  Check text if the pattern has
519 *  text arguments on nodes.  Fill labels map with pointers to nodes
520 *  in tree matched against nodes in pattern with labels.
521 */
522- (BOOL) _parse:(ANTLRCommonTree *)t1 Pattern:(ANTLRCommonTree *)aTPattern Map:(ANTLRMap *)labels
523{
524    ANTLRTreePattern *tpattern;
525    // make sure both are non-nil
526    if ( t1 == nil || aTPattern == nil ) {
527        return NO;
528    }
529    if ( [aTPattern isKindOfClass:[ANTLRWildcardTreePattern class]] ) {
530        tpattern = (ANTLRTreePattern *)aTPattern;
531    }
532    // check roots (wildcard matches anything)
533    if ( [tpattern class] != [ANTLRWildcardTreePattern class] ) {
534        if ( [adaptor getType:t1] != [tpattern type] )
535            return NO;
536        // if pattern has text, check node text
537        if ( tpattern.hasTextArg && ![[adaptor getText:t1] isEqualToString:[tpattern text]] ) {
538            return NO;
539        }
540    }
541    if ( tpattern.label != nil && labels!=nil ) {
542        // map label in pattern to node in t1
543        [labels putName:tpattern.label Node:t1];
544    }
545    // check children
546    int n1 = [adaptor getChildCount:t1];
547    int n2 = [tpattern getChildCount];
548    if ( n1 != n2 ) {
549        return NO;
550    }
551    for (int i=0; i<n1; i++) {
552        ANTLRCommonTree * child1 = [adaptor getChild:t1 At:i];
553        ANTLRCommonTree *child2 = (ANTLRCommonTree *)[tpattern getChild:i];
554        if ( ![self _parse:child1 Pattern:child2 Map:labels] ) {
555            return NO;
556        }
557    }
558    return YES;
559}
560
561/** Create a tree or node from the indicated tree pattern that closely
562 *  follows ANTLR tree grammar tree element syntax:
563 *
564 * 		(root child1 ... child2).
565 *
566 *  You can also just pass in a node: ID
567 * 
568 *  Any node can have a text argument: ID[foo]
569 *  (notice there are no quotes around foo--it's clear it's a string).
570 *
571 *  nil is a special name meaning "give me a nil node".  Useful for
572 *  making lists: (nil A B C) is a list of A B C.
573 */
574- (ANTLRCommonTree *) createTree:(NSString *)pattern
575{
576    ANTLRTreePatternLexer *tokenizer = [ANTLRTreePatternLexer newANTLRTreePatternLexer:pattern];
577    ANTLRTreePatternParser *parser = [ANTLRTreePatternParser newANTLRTreePatternParser:tokenizer Wizard:self Adaptor:adaptor];
578    ANTLRCommonTree * t = [parser pattern];
579    return t;
580}
581
582/** Compare t1 and t2; return true if token types/text, structure match exactly.
583 *  The trees are examined in their entirety so that (A B) does not match
584 *  (A B C) nor (A (B C)). 
585 // TODO: allow them to pass in a comparator
586 *  TODO: have a version that is nonstatic so it can use instance adaptor
587 *
588 *  I cannot rely on the tree node's equals() implementation as I make
589 *  no constraints at all on the node types nor interface etc... 
590 */
591- (BOOL)equals:(id)t1 O2:(id)t2 Adaptor:(id<ANTLRTreeAdaptor>)anAdaptor
592{
593    return [self _equals:t1 O2:t2 Adaptor:anAdaptor];
594}
595
596/** Compare type, structure, and text of two trees, assuming adaptor in
597 *  this instance of a TreeWizard.
598 */
599- (BOOL)equals:(id)t1 O2:(id)t2
600{
601    return [self _equals:t1 O2:t2 Adaptor:adaptor];
602}
603
604- (BOOL) _equals:(id)t1 O2:(id)t2 Adaptor:(id<ANTLRTreeAdaptor>)anAdaptor
605{
606    // make sure both are non-nil
607    if ( t1==nil || t2==nil ) {
608        return NO;
609    }
610    // check roots
611    if ( [anAdaptor getType:t1] != [anAdaptor getType:t2] ) {
612        return NO;
613    }
614    if ( ![[anAdaptor getText:t1] isEqualTo:[anAdaptor getText:t2]] ) {
615        return NO;
616    }
617    // check children
618    NSInteger n1 = [anAdaptor getChildCount:t1];
619    NSInteger n2 = [anAdaptor getChildCount:t2];
620    if ( n1 != n2 ) {
621        return NO;
622    }
623    for (int i=0; i<n1; i++) {
624        ANTLRCommonTree * child1 = [anAdaptor getChild:t1 At:i];
625        ANTLRCommonTree * child2 = [anAdaptor getChild:t2 At:i];
626        if ( ![self _equals:child1 O2:child2 Adaptor:anAdaptor] ) {
627            return NO;
628        }
629    }
630    return YES;
631}
632
633// TODO: next stuff taken from CommonTreeNodeStream
634
635/** Given a node, add this to the reverse index tokenTypeToStreamIndexesMap.
636 *  You can override this method to alter how indexing occurs.  The
637 *  default is to create a
638 *
639 *    Map<Integer token type,ArrayList<Integer stream index>>
640 *
641 *  This data structure allows you to find all nodes with type INT in order.
642 *
643 *  If you really need to find a node of type, say, FUNC quickly then perhaps
644 *
645 *    Map<Integertoken type, Map<Object tree node, Integer stream index>>
646 *
647 *  would be better for you.  The interior maps map a tree node to
648 *  the index so you don't have to search linearly for a specific node.
649 *
650 *  If you change this method, you will likely need to change
651 *  getNodeIndex(), which extracts information.
652- (void)fillReverseIndex:(ANTLRCommonTree *)node Index:(NSInteger)streamIndex
653{
654    //System.out.println("revIndex "+node+"@"+streamIndex);
655    if ( tokenTypesToReverseIndex == nil ) {
656        return; // no indexing if this is empty (nothing of interest)
657    }
658    if ( tokenTypeToStreamIndexesMap == nil ) {
659        tokenTypeToStreamIndexesMap = [ANTLRMap newANTLRMap]; // first indexing op
660    }
661    int tokenType = [adaptor getType:node];
662    Integer tokenTypeI = new Integer(tokenType);
663    if ( !(tokenTypesToReverseIndex == INDEX_ALL ||
664            [tokenTypesToReverseIndex contains:tokenTypeI]) ) {
665        return; // tokenType not of interest
666    }
667    NSInteger streamIndexI = streamIndex;
668    AMutableArray *indexes = (AMutableArray *)[tokenTypeToStreamIndexesMap objectAtIndex:tokenTypeI];
669    if ( indexes==nil ) {
670        indexes = [AMutableArray arrayWithCapacity:100]; // no list yet for this token type
671        indexes.add(streamIndexI); // not there yet, add
672        [tokenTypeToStreamIndexesMap put:tokenTypeI Idexes:indexes];
673    }
674    else {
675        if ( ![indexes contains:streamIndexI] ) {
676            [indexes add:streamIndexI]; // not there yet, add
677        }
678    }
679}
680 
681 ** Track the indicated token type in the reverse index.  Call this
682 *  repeatedly for each type or use variant with Set argument to
683 *  set all at once.
684 * @param tokenType
685public void reverseIndex:(NSInteger)tokenType
686{
687    if ( tokenTypesToReverseIndex == nil ) {
688        tokenTypesToReverseIndex = [ANTLRMap newANTLRMap];
689    }
690    else if ( tokenTypesToReverseIndex == INDEX_ALL ) {
691        return;
692    }
693    tokenTypesToReverseIndex.add(new Integer(tokenType));
694}
695 
696** Track the indicated token types in the reverse index. Set
697 *  to INDEX_ALL to track all token types.
698public void reverseIndex(Set tokenTypes) {
699    tokenTypesToReverseIndex = tokenTypes;
700}
701 
702 ** Given a node pointer, return its index into the node stream.
703 *  This is not its Token stream index.  If there is no reverse map
704 *  from node to stream index or the map does not contain entries
705 *  for node's token type, a linear search of entire stream is used.
706 *
707 *  Return -1 if exact node pointer not in stream.
708public int getNodeIndex(Object node) {
709    //System.out.println("get "+node);
710    if ( tokenTypeToStreamIndexesMap==nil ) {
711        return getNodeIndexLinearly(node);
712    }
713    int tokenType = adaptor.getType(node);
714    Integer tokenTypeI = new Integer(tokenType);
715    ArrayList indexes = (ArrayList)tokenTypeToStreamIndexesMap.get(tokenTypeI);
716    if ( indexes==nil ) {
717        //System.out.println("found linearly; stream index = "+getNodeIndexLinearly(node));
718        return getNodeIndexLinearly(node);
719    }
720    for (int i = 0; i < indexes.size(); i++) {
721        Integer streamIndexI = (Integer)indexes.get(i);
722        Object n = get(streamIndexI.intValue());
723        if ( n==node ) {
724            //System.out.println("found in index; stream index = "+streamIndexI);
725            return streamIndexI.intValue(); // found it!
726        }
727    }
728    return -1;
729}
730 
731*/
732
733@synthesize adaptor;
734@synthesize tokenNameToTypeMap;
735@end
736