// // ANTLRTreeWizard.m // ANTLR // // Created by Alan Condit on 6/18/10. // [The "BSD licence"] // Copyright (c) 2010 Alan Condit // 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. #import "ANTLRTreeWizard.h" #import "ANTLRTreePatternLexer.h" #import "ANTLRTreePatternParser.h" #import "ANTLRIntArray.h" @implementation ANTLRVisitor + (ANTLRVisitor *)newANTLRVisitor:(NSInteger)anAction Actor:(id)anActor Object:(id)anObject1 Object:(id)anObject2 { return [[ANTLRVisitor alloc] initWithAction:anAction Actor:(id)anActor Object:(id)anObject1 Object:(id)anObject2]; } - (id) initWithAction:(NSInteger)anAction Actor:(id)anActor Object:(id)anObject1 Object:(id)anObject2 { if ((self = [super init]) != nil) { action = anAction; actor = anActor; if ( actor ) [actor retain]; object1 = anObject1; if ( object1 ) [object1 retain]; object2 = anObject2; if ( object2 ) [object2 retain]; } return self; } - (void) dealloc { #ifdef DEBUG_DEALLOC NSLog( @"called dealloc in ANTLRVisitor" ); #endif if ( actor ) [actor release]; if ( object1 ) [object1 release]; if ( object2 ) [object2 release]; [super dealloc]; } - (void) visit:(ANTLRCommonTree *)t Parent:(ANTLRCommonTree *)parent ChildIndex:(NSInteger)childIndex Map:(ANTLRMap *)labels { switch (action) { case 0: [(ANTLRMap *)object2 /* labels */ clear]; if ( [(ANTLRTreeWizard *)actor _parse:t Pattern:object1/* tpattern */ Map:object2 /* labels */] ) { [self visit:t Parent:parent ChildIndex:childIndex Map:object2 /* labels */]; } break; case 1: if ( [(ANTLRTreeWizard *)actor _parse:t Pattern:object1/* tpattern */ Map:nil] ) { [(AMutableArray *)object2/* subtrees */ addObject:t]; } break; } // [self visit:t]; return; } - (void) visit:(ANTLRCommonTree *)t { [object1 addObject:t]; return; } @synthesize action; @synthesize actor; @synthesize object1; @synthesize object2; @end /** When using %label:TOKENNAME in a tree for parse(), we must * track the label. */ @implementation ANTLRTreePattern @synthesize label; @synthesize hasTextArg; + (ANTLRCommonTree *)newANTLRTreePattern:(id)payload { return (ANTLRCommonTree *)[[ANTLRTreePattern alloc] initWithToken:payload]; } - (id) initWithToken:(id)payload { self = [super initWithToken:payload]; if ( self != nil ) { } return (ANTLRCommonTree *)self; } - (void) dealloc { #ifdef DEBUG_DEALLOC NSLog( @"called dealloc in ANTLRTreePattern" ); #endif if ( label ) [label release]; [super dealloc]; } - (NSString *)toString { if ( label != nil ) { return [NSString stringWithFormat:@"\% %@ : %@", label, [super toString]]; } else { return [super toString]; } } @end @implementation ANTLRWildcardTreePattern + (ANTLRWildcardTreePattern *)newANTLRWildcardTreePattern:(id)payload { return(ANTLRWildcardTreePattern *)[[ANTLRWildcardTreePattern alloc] initWithToken:(id)payload]; } - (id) initWithToken:(id)payload { self = [super initWithToken:payload]; if ( self != nil ) { } return self; } @end /** This adaptor creates TreePattern objects for use during scan() */ @implementation ANTLRTreePatternTreeAdaptor + (ANTLRTreePatternTreeAdaptor *)newTreeAdaptor { return [[ANTLRTreePatternTreeAdaptor alloc] init]; } - (id) init { self = [super init]; if ( self != nil ) { } return self; } - (ANTLRCommonTree *)createTreePattern:(id)payload { return (ANTLRCommonTree *)[super create:payload]; } @end @implementation ANTLRTreeWizard // TODO: build indexes for the wizard /** 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. protected Map tokenTypeToStreamIndexesMap; ** If tokenTypesToReverseIndex set to INDEX_ALL then indexing * occurs for all token types. public static final Set INDEX_ALL = new HashSet(); ** 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 nil, * then none are indexed. protected Set tokenTypesToReverseIndex = nil; */ + (ANTLRTreeWizard *) newANTLRTreeWizard:(id)anAdaptor { return [[ANTLRTreeWizard alloc] initWithAdaptor:anAdaptor]; } + (ANTLRTreeWizard *)newANTLRTreeWizard:(id)anAdaptor Map:(ANTLRMap *)aTokenNameToTypeMap { return [[ANTLRTreeWizard alloc] initWithAdaptor:anAdaptor Map:aTokenNameToTypeMap]; } + (ANTLRTreeWizard *)newANTLRTreeWizard:(id)anAdaptor TokenNames:(NSArray *)theTokNams { return [[ANTLRTreeWizard alloc] initWithTokenNames:anAdaptor TokenNames:theTokNams]; } + (ANTLRTreeWizard *)newANTLRTreeWizardWithTokenNames:(NSArray *)theTokNams { return [[ANTLRTreeWizard alloc] initWithTokenNames:theTokNams]; } - (id) init { if ((self = [super init]) != nil) { } return self; } - (id) initWithAdaptor:(id)anAdaptor { if ((self = [super init]) != nil) { adaptor = anAdaptor; if ( adaptor ) [adaptor retain]; } return self; } - (id) initWithAdaptor:(id)anAdaptor Map:(ANTLRMap *)aTokenNameToTypeMap { if ((self = [super init]) != nil) { adaptor = anAdaptor; if ( adaptor ) [adaptor retain]; tokenNameToTypeMap = aTokenNameToTypeMap; } return self; } - (id) initWithTokenNames:(NSArray *)theTokNams { if ((self = [super init]) != nil) { #pragma warning Fix initWithTokenNames. // adaptor = anAdaptor; //tokenNameToTypeMap = aTokenNameToTypeMap; tokenNameToTypeMap = [[self computeTokenTypes:theTokNams] retain]; } return self; } - (id) initWithTokenNames:(id)anAdaptor TokenNames:(NSArray *)theTokNams { if ((self = [super init]) != nil) { adaptor = anAdaptor; if ( adaptor ) [adaptor retain]; // tokenNameToTypeMap = aTokenNameToTypeMap; tokenNameToTypeMap = [[self computeTokenTypes:theTokNams] retain]; } return self; } - (void) dealloc { #ifdef DEBUG_DEALLOC NSLog( @"called dealloc in ANTLRTreePatternTreeAdaptor" ); #endif if ( adaptor ) [adaptor release]; if ( tokenNameToTypeMap ) [tokenNameToTypeMap release]; [super dealloc]; } /** Compute a Map that is an inverted index of * tokenNames (which maps int token types to names). */ - (ANTLRMap *)computeTokenTypes:(NSArray *)theTokNams { ANTLRMap *m = [ANTLRMap newANTLRMap]; if ( theTokNams == nil ) { return m; } for (int ttype = ANTLRTokenTypeMIN; ttype < [theTokNams count]; ttype++) { NSString *name = (NSString *) [theTokNams objectAtIndex:ttype]; [m putName:name TType:ttype]; } return m; } /** Using the map of token names to token types, return the type. */ - (NSInteger)getTokenType:(NSString *)tokenName { if ( tokenNameToTypeMap == nil ) { return ANTLRTokenTypeInvalid; } NSInteger aTType = (NSInteger)[tokenNameToTypeMap getTType:tokenName]; if ( aTType != -1 ) { return aTType; } return ANTLRTokenTypeInvalid; } /** 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 where the List is * of your AST node type. The Integer is the token type of the node. * * TODO: save this index so that find and visit are faster */ - (ANTLRMap *)index:(ANTLRCommonTree *)t { ANTLRMap *m = [ANTLRMap newANTLRMap]; [self _index:t Map:m]; return m; } /** Do the work for index */ - (void) _index:(ANTLRCommonTree *)t Map:(ANTLRMap *)m { if ( t==nil ) { return; } #pragma warning Fix _index use of ANTLRMap. NSInteger ttype = [adaptor getType:t]; ANTLRMap *elements = (ANTLRMap *)[m getName:ttype]; if ( elements == nil ) { elements = [ANTLRMap newANTLRMapWithLen:100]; [m putNode:ttype Node:elements]; } [elements addObject:t]; int n = [adaptor getChildCount:t]; for (int i=0; i)anAdaptor { return [self _equals:t1 O2:t2 Adaptor:anAdaptor]; } /** Compare type, structure, and text of two trees, assuming adaptor in * this instance of a TreeWizard. */ - (BOOL)equals:(id)t1 O2:(id)t2 { return [self _equals:t1 O2:t2 Adaptor:adaptor]; } - (BOOL) _equals:(id)t1 O2:(id)t2 Adaptor:(id)anAdaptor { // make sure both are non-nil if ( t1==nil || t2==nil ) { return NO; } // check roots if ( [anAdaptor getType:t1] != [anAdaptor getType:t2] ) { return NO; } if ( ![[anAdaptor getText:t1] isEqualTo:[anAdaptor getText:t2]] ) { return NO; } // check children NSInteger n1 = [anAdaptor getChildCount:t1]; NSInteger n2 = [anAdaptor getChildCount:t2]; if ( n1 != n2 ) { return NO; } for (int i=0; i> * * 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> * * 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. - (void)fillReverseIndex:(ANTLRCommonTree *)node Index:(NSInteger)streamIndex { //System.out.println("revIndex "+node+"@"+streamIndex); if ( tokenTypesToReverseIndex == nil ) { return; // no indexing if this is empty (nothing of interest) } if ( tokenTypeToStreamIndexesMap == nil ) { tokenTypeToStreamIndexesMap = [ANTLRMap newANTLRMap]; // first indexing op } int tokenType = [adaptor getType:node]; Integer tokenTypeI = new Integer(tokenType); if ( !(tokenTypesToReverseIndex == INDEX_ALL || [tokenTypesToReverseIndex contains:tokenTypeI]) ) { return; // tokenType not of interest } NSInteger streamIndexI = streamIndex; AMutableArray *indexes = (AMutableArray *)[tokenTypeToStreamIndexesMap objectAtIndex:tokenTypeI]; if ( indexes==nil ) { indexes = [AMutableArray arrayWithCapacity:100]; // no list yet for this token type indexes.add(streamIndexI); // not there yet, add [tokenTypeToStreamIndexesMap put:tokenTypeI Idexes:indexes]; } else { if ( ![indexes contains:streamIndexI] ) { [indexes add:streamIndexI]; // not there yet, add } } } ** 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. * @param tokenType public void reverseIndex:(NSInteger)tokenType { if ( tokenTypesToReverseIndex == nil ) { tokenTypesToReverseIndex = [ANTLRMap newANTLRMap]; } else if ( tokenTypesToReverseIndex == INDEX_ALL ) { return; } tokenTypesToReverseIndex.add(new Integer(tokenType)); } ** Track the indicated token types in the reverse index. Set * to INDEX_ALL to track all token types. public void reverseIndex(Set tokenTypes) { tokenTypesToReverseIndex = tokenTypes; } ** 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. * * Return -1 if exact node pointer not in stream. public int getNodeIndex(Object node) { //System.out.println("get "+node); if ( tokenTypeToStreamIndexesMap==nil ) { return getNodeIndexLinearly(node); } int tokenType = adaptor.getType(node); Integer tokenTypeI = new Integer(tokenType); ArrayList indexes = (ArrayList)tokenTypeToStreamIndexesMap.get(tokenTypeI); if ( indexes==nil ) { //System.out.println("found linearly; stream index = "+getNodeIndexLinearly(node)); return getNodeIndexLinearly(node); } for (int i = 0; i < indexes.size(); i++) { Integer streamIndexI = (Integer)indexes.get(i); Object n = get(streamIndexI.intValue()); if ( n==node ) { //System.out.println("found in index; stream index = "+streamIndexI); return streamIndexI.intValue(); // found it! } } return -1; } */ @synthesize adaptor; @synthesize tokenNameToTypeMap; @end