Home | History | Annotate | Download | only in Framework
      1 //
      2 //  ANTLRTokenRewriteStream.m
      3 //  ANTLR
      4 //
      5 //  Created by Alan Condit on 6/19/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 "ANTLRTokenRewriteStream.h"
     33 #import "ANTLRRuntimeException.h"
     34 
     35 static NSString *DEFAULT_PROGRAM_NAME = @"default";
     36 static NSInteger PROGRAM_INIT_SIZE = 100;
     37 static NSInteger MIN_TOKEN_INDEX = 0;
     38 
     39 extern NSInteger debug;
     40 
     41 // Define the rewrite operation hierarchy
     42 
     43 @implementation ANTLRRewriteOperation
     44 
     45 @synthesize instructionIndex;
     46 @synthesize rwIndex;
     47 @synthesize text;
     48 
     49 + (ANTLRRewriteOperation *) newANTLRRewriteOperation:(NSInteger)anIndex Text:(NSString *)theText
     50 {
     51     return [[ANTLRRewriteOperation alloc] initWithIndex:anIndex Text:theText];
     52 }
     53     
     54 - (id) initWithIndex:(NSInteger)anIndex Text:(NSString *)theText
     55 {
     56     if ((self = [super init]) != nil) {
     57         rwIndex = anIndex;
     58         text = theText;
     59     }
     60     return self;
     61 }
     62 
     63 /** Execute the rewrite operation by possibly adding to the buffer.
     64  *  Return the rwIndex of the next token to operate on.
     65  */
     66 - (NSInteger) execute:(NSString *)buf
     67 {
     68     return rwIndex;
     69 }
     70     
     71 - (NSString *)toString
     72 {
     73     NSString *opName = [self className];
     74     int $index = [self indexOf:'$' inString:opName];
     75     opName = [opName substringWithRange:NSMakeRange($index+1, [opName length])];
     76     return [NSString stringWithFormat:@"<%@%d:\"%@\">", opName, rwIndex, opName];			
     77 }
     78 
     79 - (NSInteger) indexOf:(char)aChar inString:(NSString *)aString
     80 {
     81     char indexedChar;
     82 
     83     for( int i = 0; i < [aString length]; i++ ) {
     84         indexedChar = [aString characterAtIndex:i];
     85         if (indexedChar == aChar) {
     86             return i;
     87         }
     88     }
     89     return -1;
     90 }
     91                                                     
     92 @end
     93 
     94 @implementation ANTLRInsertBeforeOp
     95 
     96 + (ANTLRInsertBeforeOp *) newANTLRInsertBeforeOp:(NSInteger) anIndex Text:(NSString *)theText
     97 {
     98     return [[ANTLRInsertBeforeOp alloc] initWithIndex:anIndex Text:theText];
     99 }
    100 
    101 - (id) initWithIndex:(NSInteger)anIndex Text:(NSString *)theText
    102 {
    103     if ((self = [super initWithIndex:anIndex Text:theText]) != nil) {
    104         rwIndex = anIndex;
    105         text = theText;
    106     }
    107     return self;
    108 }
    109 
    110 
    111 - (NSInteger) execute:(NSMutableString *)buf
    112 {
    113     [buf appendString:text];
    114     if ( ((ANTLRCommonToken *)[tokens objectAtIndex:rwIndex]).type != ANTLRTokenTypeEOF ) {
    115         [buf appendString:[[tokens objectAtIndex:rwIndex] text]];
    116     }
    117     return rwIndex+1;
    118 }
    119 
    120 @end
    121      
    122 /** I'm going to try replacing range from x..y with (y-x)+1 ANTLRReplaceOp
    123  *  instructions.
    124  */
    125 @implementation ANTLRReplaceOp
    126 
    127 @synthesize lastIndex;
    128 
    129 + (ANTLRReplaceOp *) newANTLRReplaceOp:(NSInteger)from ToIndex:(NSInteger)to Text:(NSString*)theText
    130 {
    131     return [[ANTLRReplaceOp alloc] initWithIndex:from ToIndex:to Text:theText];
    132 }
    133 
    134 - (id) initWithIndex:(NSInteger)from ToIndex:(NSInteger)to Text:(NSString *)theText
    135 {
    136     if ((self = [super initWithIndex:from Text:theText]) != nil) {
    137         lastIndex = to;
    138     }
    139     return self;
    140 }
    141  
    142  
    143 - (NSInteger) execute:(NSMutableString *)buf
    144 {
    145     if ( text!=nil ) {
    146         [buf appendString:text];
    147     }
    148         return lastIndex+1;
    149 }
    150 
    151 - (NSString *)toString
    152 {
    153     return [NSString stringWithFormat:@"<ANTLRReplaceOp@ %d..%d :>%@\n", rwIndex, lastIndex, text];
    154 }
    155 
    156 @end
    157 
    158 @implementation ANTLRDeleteOp
    159 
    160 + (ANTLRDeleteOp *) newANTLRDeleteOp:(NSInteger)from ToIndex:(NSInteger)to
    161 {
    162     // super(from To:to, null);
    163     return [[ANTLRDeleteOp alloc] initWithIndex:from ToIndex:to];
    164 }
    165 
    166  - (id) initWithIndex:(NSInteger)from ToIndex:(NSInteger)to
    167 {
    168     if ((self = [super initWithIndex:from ToIndex:to Text:nil]) != nil) {
    169         lastIndex = to;
    170     }
    171     return self;
    172 }
    173      
    174 - (NSString *)toString
    175 {
    176     return [NSString stringWithFormat:@"<DeleteOp@ %d..%d\n",  rwIndex, lastIndex];
    177 }
    178 
    179 @end
    180 
    181 
    182 @implementation ANTLRTokenRewriteStream
    183 
    184 @synthesize programs;
    185 @synthesize lastRewriteTokenIndexes;
    186 
    187 + (ANTLRTokenRewriteStream *)newANTLRTokenRewriteStream
    188 {
    189     return [[ANTLRTokenRewriteStream alloc] init];
    190 }
    191 
    192 + (ANTLRTokenRewriteStream *)newANTLRTokenRewriteStream:(id<ANTLRTokenSource>) aTokenSource
    193 {
    194     return [[ANTLRTokenRewriteStream alloc] initWithTokenSource:aTokenSource];
    195 }
    196 
    197 + (ANTLRTokenRewriteStream *)newANTLRTokenRewriteStream:(id<ANTLRTokenSource>) aTokenSource Channel:(NSInteger)aChannel
    198 {
    199     return [[ANTLRTokenRewriteStream alloc] initWithTokenSource:aTokenSource Channel:aChannel];
    200 }
    201  
    202 - (id) init
    203 {
    204     if ((self = [super init]) != nil) {
    205         programs = [ANTLRHashMap newANTLRHashMap];
    206         [programs addObject:[ANTLRMapElement newANTLRMapElementWithName:DEFAULT_PROGRAM_NAME Node:[ANTLRHashMap newANTLRHashMapWithLen:PROGRAM_INIT_SIZE]]];
    207         lastRewriteTokenIndexes = [ANTLRHashMap newANTLRHashMap];
    208     }
    209     return self;
    210 }
    211  
    212 - (id)initWithTokenSource:(id<ANTLRTokenSource>)aTokenSource
    213 {
    214     if ((self = [super init]) != nil) {
    215         programs = [ANTLRHashMap newANTLRHashMap];
    216         [programs addObject:[ANTLRMapElement newANTLRMapElementWithName:DEFAULT_PROGRAM_NAME Node:[ANTLRHashMap newANTLRHashMapWithLen:PROGRAM_INIT_SIZE]]];
    217         lastRewriteTokenIndexes = [ANTLRHashMap newANTLRHashMap];
    218         tokenSource = aTokenSource;
    219     }
    220     return self;
    221 }
    222 
    223 - (id)initWithTokenSource:(id<ANTLRTokenSource>)aTokenSource Channel:(NSInteger)aChannel
    224 {
    225     if ((self = [super init]) != nil) {
    226         programs = [ANTLRHashMap newANTLRHashMap];
    227         [programs addObject:[ANTLRMapElement newANTLRMapElementWithName:DEFAULT_PROGRAM_NAME Node:[ANTLRHashMap newANTLRHashMapWithLen:PROGRAM_INIT_SIZE]]];
    228         lastRewriteTokenIndexes = [ANTLRHashMap newANTLRHashMap];
    229         tokenSource = aTokenSource;
    230         channel = aChannel;
    231     }
    232     return self;
    233 }
    234  
    235 - (ANTLRHashMap *)getPrograms
    236 {
    237     return programs;
    238 }
    239  
    240 - (void)setPrograms:(ANTLRHashMap *)aProgList
    241 {
    242     programs = aProgList;
    243 }
    244 
    245 - (void) rollback:(NSInteger)instructionIndex
    246 {
    247     [self rollback:DEFAULT_PROGRAM_NAME Index:instructionIndex];
    248 }
    249 
    250 /** Rollback the instruction stream for a program so that
    251  *  the indicated instruction (via instructionIndex) is no
    252  *  longer in the stream.  UNTESTED!
    253  */
    254 - (void) rollback:(NSString *)programName Index:(NSInteger)anInstructionIndex
    255 {
    256     id object;
    257     ANTLRHashMap *is;
    258 
    259     //    AMutableArray *is = [programs get(programName)];
    260     is = [self getPrograms];
    261     object = [is getName:programName];
    262     if ( is != nil ) {
    263 #pragma warning this has to be fixed
    264         [programs insertObject:programName  atIndex:anInstructionIndex];
    265     }
    266 }
    267 
    268 - (void) deleteProgram
    269 {
    270     [self deleteProgram:DEFAULT_PROGRAM_NAME];
    271 }
    272 
    273 /** Reset the program so that no instructions exist */
    274 - (void) deleteProgram:(NSString *)programName
    275 {
    276     [self rollback:programName Index:MIN_TOKEN_INDEX];
    277 }
    278 
    279 - (void) insertAfterToken:(id<ANTLRToken>)t Text:(NSString *)theText
    280 {
    281     [self insertAfterProgNam:DEFAULT_PROGRAM_NAME Index:[t getTokenIndex] Text:theText];
    282 }
    283 
    284 - (void) insertAfterIndex:(NSInteger)anIndex Text:(NSString *)theText
    285 {
    286     [self insertAfterProgNam:DEFAULT_PROGRAM_NAME Index:(NSInteger)anIndex Text:(NSString *)theText];
    287 }
    288 
    289 - (void) insertAfterProgNam:(NSString *)programName Index:(NSInteger)anIndex Text:(NSString *)theText
    290 {
    291     // to insert after, just insert before next rwIndex (even if past end)
    292     [self insertBeforeProgName:programName Index:anIndex+1 Text:theText];
    293     //addToSortedRewriteList(programName, new InsertAfterOp(rwIndex,text));
    294 }
    295 
    296 
    297 
    298 
    299 
    300 
    301 
    302 
    303 
    304 - (void) insertBeforeToken:(id<ANTLRToken>)t Text:(NSString *)theText
    305 {
    306     [self insertBeforeProgName:DEFAULT_PROGRAM_NAME Index:[t getTokenIndex] Text:theText];
    307 }
    308 
    309 - (void) insertBeforeIndex:(NSInteger)anIndex Text:(NSString *)theText
    310 {
    311     [self insertBeforeProgName:DEFAULT_PROGRAM_NAME Index:anIndex Text:theText];
    312 }
    313 
    314 - (void) insertBeforeProgName:(NSString *)programName Index:(NSInteger)rwIndex Text:(NSString *)theText
    315 {
    316     //addToSortedRewriteList(programName, new ANTLRInsertBeforeOp(rwIndex,text));
    317     ANTLRRewriteOperation *op = [ANTLRInsertBeforeOp newANTLRInsertBeforeOp:rwIndex Text:theText];
    318     ANTLRHashMap *rewrites = [self getProgram:programName];
    319     op.instructionIndex = [rewrites count];
    320     [rewrites addObject:op];		
    321 }
    322 
    323 - (void) replaceFromIndex:(NSInteger)anIndex Text:(NSString *)theText
    324 {
    325     [self replaceProgNam:DEFAULT_PROGRAM_NAME FromIndex:anIndex ToIndex:anIndex Text:theText];
    326 }
    327 
    328 - (void) replaceFromIndex:(NSInteger)from ToIndex:(NSInteger)to Text:(NSString *)theText
    329 {
    330     [self replaceProgNam:DEFAULT_PROGRAM_NAME FromIndex:from ToIndex:to Text:theText];
    331 }
    332 
    333 - (void) replaceFromToken:(id<ANTLRToken>)anIndexT Text:(NSString *)theText
    334 {
    335     [self replaceProgNam:DEFAULT_PROGRAM_NAME FromIndex:[anIndexT getTokenIndex] ToIndex:[anIndexT getTokenIndex] Text:theText];
    336 }
    337 
    338 - (void) replaceFromToken:(id<ANTLRToken>)from ToToken:(id<ANTLRToken>)to Text:(NSString *)theText
    339 {
    340     [self replaceProgNam:DEFAULT_PROGRAM_NAME FromIndex:[from getTokenIndex] ToIndex:[to getTokenIndex] Text:theText];
    341 }
    342 
    343 - (void) replaceProgNam:(NSString *)programName Token:(id<ANTLRToken>)from Token:(id<ANTLRToken>)to Text:(NSString *)theText
    344 {
    345     [self replaceProgNam:programName FromIndex:[from getTokenIndex] ToIndex:[to getTokenIndex] Text:theText];
    346 }
    347                          
    348 - (void) replaceProgNam:(NSString *)programName FromIndex:(NSInteger)from ToIndex:(NSInteger)to Text:(NSString *)theText
    349 {
    350     if ( from > to || from < 0 || to < 0 || to >= [tokens count] ) {
    351         @throw [ANTLRIllegalArgumentException newException:[NSString stringWithFormat:@"replace: range invalid: %d..%d size=%d\n", from, to, [tokens count]]];
    352     }
    353     ANTLRRewriteOperation *op = [ANTLRReplaceOp newANTLRReplaceOp:from ToIndex:to Text:theText];
    354     ANTLRHashMap *rewrites = (ANTLRHashMap *)[lastRewriteTokenIndexes getName:programName];
    355     op.instructionIndex = [rewrites count];
    356     [rewrites addObject:op];
    357 }
    358 
    359 - (void) delete:(NSInteger)anIndex
    360 {
    361     [self delete:DEFAULT_PROGRAM_NAME  FromIndex:(NSInteger)anIndex  ToIndex:(NSInteger)anIndex];
    362 }
    363 
    364 - (void) delete:(NSInteger)from ToIndex:(NSInteger)to
    365 {
    366     [self delete:DEFAULT_PROGRAM_NAME FromIndex:from ToIndex:to];
    367 }
    368 
    369 - (void) deleteToken:(id<ANTLRToken>)anIndexT
    370 {
    371     [self delete:DEFAULT_PROGRAM_NAME FromIndex:[anIndexT getTokenIndex] ToIndex:[anIndexT getTokenIndex]];
    372 }
    373 
    374 - (void) deleteFromToken:(id<ANTLRToken>)from ToToken:(id<ANTLRToken>)to
    375 {
    376     [self delete:DEFAULT_PROGRAM_NAME FromIndex:[from getTokenIndex] ToIndex:[to getTokenIndex]];
    377 }
    378 
    379 - (void) delete:(NSString *)programName FromToken:(id<ANTLRToken>)from ToToken:(id<ANTLRToken>)to
    380 {
    381     [self replaceProgNam:programName FromIndex:[from getTokenIndex] ToIndex:[to getTokenIndex] Text:nil];
    382 }
    383 
    384 - (void) delete:(NSString *)programName FromIndex:(NSInteger)from ToIndex:(NSInteger)to
    385 {
    386     [self replaceProgNam:programName FromIndex:from ToIndex:to Text:nil];
    387 }
    388 
    389 - (NSInteger)getLastRewriteTokenIndex
    390 {
    391     return [self getLastRewriteTokenIndex:DEFAULT_PROGRAM_NAME];
    392 }
    393 
    394 - (NSInteger)getLastRewriteTokenIndex:(NSString *)programName
    395 {
    396 #pragma warning fix this to look up the hashed name
    397     NSInteger anInt = -1;
    398     ANTLRMapElement *node = [lastRewriteTokenIndexes lookup:programName Scope:0];
    399     if ( node != nil ) {
    400         anInt = [lastRewriteTokenIndexes hash:programName];
    401     }
    402     return anInt;
    403 }
    404 
    405 - (void)setLastRewriteTokenIndex:(NSString *)programName Index:(NSInteger)anInt
    406 {
    407     [lastRewriteTokenIndexes insertObject:programName atIndex:anInt];
    408 }
    409 
    410 -(ANTLRHashMap *) getProgram:(NSString *)name
    411 {
    412    ANTLRHashMap *is = (ANTLRHashMap *)[programs getName:name];
    413     if ( is == nil ) {
    414         is = [self initializeProgram:name];
    415     }
    416     return is;
    417 }
    418 
    419 -(ANTLRHashMap *) initializeProgram:(NSString *)name
    420 {
    421     ANTLRHashMap *is = [ANTLRHashMap newANTLRHashMapWithLen:PROGRAM_INIT_SIZE];
    422     [is putName:name Node:nil];
    423     return is;
    424 }
    425 
    426 - (NSString *)toOriginalString
    427 {
    428     [super fill];
    429     return [self toOriginalString:MIN_TOKEN_INDEX End:[tokens count]-1];
    430 }
    431 
    432 - (NSString *)toOriginalString:(NSInteger)start End:(NSInteger)end
    433 {
    434     NSMutableString *buf = [NSMutableString stringWithCapacity:100];
    435     for (int i = start; i >= MIN_TOKEN_INDEX && i <= end && i< [tokens count]; i++) {
    436         if ( [[lastRewriteTokenIndexes objectAtIndex:i] type] != ANTLRTokenTypeEOF )
    437             [buf appendString:[[tokens objectAtIndex:i] text]];
    438     }
    439     return [NSString stringWithString:buf];
    440 }
    441 
    442 - (NSString *)toString
    443 {
    444     [super fill];
    445     return [self toStringFromStart:MIN_TOKEN_INDEX ToEnd:[tokens count]-1];
    446 }
    447 
    448 - (NSString *)toString:(NSString *)programName
    449 {
    450     [super fill];
    451     return [self toString:programName FromStart:MIN_TOKEN_INDEX ToEnd:[[programs objectAtIndex:MIN_TOKEN_INDEX] count]-1];
    452 }
    453 
    454 - (NSString *)toStringFromStart:(NSInteger)start ToEnd:(NSInteger)end
    455 {
    456     return [self toString:DEFAULT_PROGRAM_NAME FromStart:start ToEnd:end];
    457 }
    458 
    459 - (NSString *)toString:(NSString *)programName FromStart:(NSInteger)start ToEnd:(NSInteger)end
    460 {
    461     ANTLRHashMap *rewrites = (ANTLRHashMap *)[programs getName:programName];
    462     
    463     // ensure start/end are in range
    464     if ( end > [tokens count]-1 ) end = [tokens count]-1;
    465     if ( start < 0 )
    466         start = 0;
    467     
    468     if ( rewrites == nil || [rewrites count] == 0 ) {
    469         return [self toOriginalString:start End:end]; // no instructions to execute
    470     }
    471     NSMutableString *buf = [NSMutableString stringWithCapacity:100];
    472     
    473     // First, optimize instruction stream
    474     ANTLRHashMap *indexToOp = [self reduceToSingleOperationPerIndex:rewrites];
    475     
    476     // Walk buffer, executing instructions and emitting tokens
    477     int i = start;
    478     while ( i <= end && i < [tokens count] ) {
    479         ANTLRRewriteOperation *op = (ANTLRRewriteOperation *)[indexToOp objectAtIndex:i];
    480         [indexToOp setObject:nil atIndex:i]; // remove so any left have rwIndex size-1
    481         id<ANTLRToken>t = (id<ANTLRToken>) [tokens objectAtIndex:i];
    482         if ( op == nil ) {
    483             // no operation at that rwIndex, just dump token
    484             if ( t.type != ANTLRTokenTypeEOF )
    485                 [buf appendString:t.text];
    486             i++; // move to next token
    487         }
    488         else {
    489             i = [op execute:buf]; // execute operation and skip
    490         }
    491     }
    492     
    493     // include stuff after end if it's last rwIndex in buffer
    494     // So, if they did an insertAfter(lastValidIndex, "foo"), include
    495     // foo if end==lastValidIndex.
    496     //if ( end == [tokens size]-1 ) {
    497     if ( end == [tokens count]-1 ) {
    498         // Scan any remaining operations after last token
    499         // should be included (they will be inserts).
    500         int i2 = 0;
    501         while ( i2 < [indexToOp count] - 1 ) {
    502             ANTLRRewriteOperation *op = [indexToOp objectAtIndex:i2];
    503             if ( op.rwIndex >= [tokens count]-1 ) {
    504                 [buf appendString:op.text];
    505             }
    506         }
    507     }
    508     return [NSString stringWithString:buf];
    509 }
    510 
    511 /** We need to combine operations and report invalid operations (like
    512  *  overlapping replaces that are not completed nested).  Inserts to
    513  *  same rwIndex need to be combined etc...   Here are the cases:
    514  *
    515  *  I.i.u I.j.v								leave alone, nonoverlapping
    516  *  I.i.u I.i.v								combine: Iivu
    517  *
    518  *  R.i-j.u R.x-y.v	| i-j in x-y			delete first R
    519  *  R.i-j.u R.i-j.v							delete first R
    520  *  R.i-j.u R.x-y.v	| x-y in i-j			ERROR
    521  *  R.i-j.u R.x-y.v	| boundaries overlap	ERROR
    522  *
    523  *  I.i.u R.x-y.v | i in x-y				delete I
    524  *  I.i.u R.x-y.v | i not in x-y			leave alone, nonoverlapping
    525  *  R.x-y.v I.i.u | i in x-y				ERROR
    526  *  R.x-y.v I.x.u 							R.x-y.uv (combine, delete I)
    527  *  R.x-y.v I.i.u | i not in x-y			leave alone, nonoverlapping
    528  *
    529  *  I.i.u = insert u before op @ rwIndex i
    530  *  R.x-y.u = replace x-y indexed tokens with u
    531  *
    532  *  First we need to examine replaces.  For any replace op:
    533  *
    534  * 		1. wipe out any insertions before op within that range.
    535  *		2. Drop any replace op before that is contained completely within
    536  *         that range.
    537  *		3. Throw exception upon boundary overlap with any previous replace.
    538  *
    539  *  Then we can deal with inserts:
    540  *
    541  * 		1. for any inserts to same rwIndex, combine even if not adjacent.
    542  * 		2. for any prior replace with same left boundary, combine this
    543  *         insert with replace and delete this replace.
    544  * 		3. throw exception if rwIndex in same range as previous replace
    545  *
    546  *  Don't actually delete; make op null in list. Easier to walk list.
    547  *  Later we can throw as we add to rwIndex -> op map.
    548  *
    549  *  Note that I.2 R.2-2 will wipe out I.2 even though, technically, the
    550  *  inserted stuff would be before the replace range.  But, if you
    551  *  add tokens in front of a method body '{' and then delete the method
    552  *  body, I think the stuff before the '{' you added should disappear too.
    553  *
    554  *  Return a map from token rwIndex to operation.
    555  */
    556 - (ANTLRHashMap *)reduceToSingleOperationPerIndex:(ANTLRHashMap *)rewrites
    557 {
    558     //System.out.println("rewrites="+rewrites);
    559     if (debug > 1) NSLog(@"rewrites=%@\n", [rewrites getName:DEFAULT_PROGRAM_NAME]);
    560     // WALK REPLACES
    561     for (int i = 0; i < [rewrites count]; i++) {
    562         ANTLRRewriteOperation *op = (ANTLRRewriteOperation *)[rewrites objectAtIndex:i];
    563         if ( op==nil )
    564             continue;
    565         if ( !([[op class] isKindOfClass:[ANTLRReplaceOp class]]) )
    566             continue;
    567         ANTLRReplaceOp *rop = (ANTLRReplaceOp *)[rewrites objectAtIndex:i];
    568         // Wipe prior inserts within range
    569         //List inserts = getKindOfOps(rewrites, ANTLRInsertBeforeOp.class, i);
    570         ANTLRHashMap *inserts = [self getKindOfOps:rewrites KindOfClass:[ANTLRInsertBeforeOp class] Index:i];
    571         for (int j = 0; j < [inserts size]; j++) {
    572             ANTLRInsertBeforeOp *iop = (ANTLRInsertBeforeOp *)[inserts objectAtIndex:j];
    573             if ( iop.rwIndex >= rop.rwIndex && iop.rwIndex <= rop.lastIndex ) {
    574                 // delete insert as it's a no-op.
    575                 [rewrites insertObject:nil atIndex:iop.instructionIndex];
    576             }
    577         }
    578         // Drop any prior replaces contained within
    579         ANTLRHashMap *prevReplaces = [self getKindOfOps:rewrites KindOfClass:[ANTLRReplaceOp class] Index:i];
    580         for (int j = 0; j < [prevReplaces count]; j++) {
    581             ANTLRReplaceOp *prevRop = (ANTLRReplaceOp *) [prevReplaces objectAtIndex:j];
    582             if ( prevRop.rwIndex>=rop.rwIndex && prevRop.lastIndex <= rop.lastIndex ) {
    583                 // delete replace as it's a no-op.
    584                 [rewrites setObject:nil atIndex:prevRop.instructionIndex];
    585                 continue;
    586             }
    587             // throw exception unless disjoint or identical
    588             BOOL disjoint = prevRop.lastIndex<rop.rwIndex || prevRop.rwIndex > rop.lastIndex;
    589             BOOL same = prevRop.rwIndex==rop.rwIndex && prevRop.lastIndex==rop.lastIndex;
    590             if ( !disjoint && !same ) {
    591                 @throw [ANTLRIllegalArgumentException newException:
    592                         [NSString stringWithFormat:@"replace op boundaries of %@, overlap with previous %@\n", rop, prevRop]];
    593             }
    594         }
    595     }
    596     
    597     // WALK INSERTS
    598     for (int i = 0; i < [rewrites count]; i++) {
    599         ANTLRRewriteOperation *op = (ANTLRRewriteOperation *)[rewrites objectAtIndex:i];
    600         if ( op == nil )
    601             continue;
    602         if ( !([[op class] isKindOfClass:[ANTLRInsertBeforeOp class]]) )
    603             continue;
    604         ANTLRInsertBeforeOp *iop = (ANTLRInsertBeforeOp *)[rewrites objectAtIndex:i];
    605         // combine current insert with prior if any at same rwIndex
    606         ANTLRHashMap *prevInserts = (ANTLRHashMap *)[self getKindOfOps:rewrites KindOfClass:[ANTLRInsertBeforeOp class] Index:i];
    607         for (int j = 0; j < [prevInserts count]; j++) {
    608             ANTLRInsertBeforeOp *prevIop = (ANTLRInsertBeforeOp *) [prevInserts objectAtIndex:j];
    609             if ( prevIop.rwIndex == iop.rwIndex ) { // combine objects
    610                                                 // convert to strings...we're in process of toString'ing
    611                                                 // whole token buffer so no lazy eval issue with any templates
    612                 iop.text = [self catOpText:iop.text PrevText:prevIop.text];
    613                 // delete redundant prior insert
    614                 [rewrites setObject:nil atIndex:prevIop.instructionIndex];
    615             }
    616         }
    617         // look for replaces where iop.rwIndex is in range; error
    618         ANTLRHashMap *prevReplaces = (ANTLRHashMap *)[self getKindOfOps:rewrites KindOfClass:[ANTLRReplaceOp class] Index:i];
    619         for (int j = 0; j < [prevReplaces count]; j++) {
    620             ANTLRReplaceOp *rop = (ANTLRReplaceOp *) [prevReplaces objectAtIndex:j];
    621             if ( iop.rwIndex == rop.rwIndex ) {
    622                 rop.text = [self catOpText:iop.text PrevText:rop.text];
    623                 [rewrites setObject:nil atIndex:i];  // delete current insert
    624                 continue;
    625             }
    626             if ( iop.rwIndex >= rop.rwIndex && iop.rwIndex <= rop.lastIndex ) {
    627                 @throw [ANTLRIllegalArgumentException newException:[NSString stringWithFormat:@"insert op %d within boundaries of previous %d", iop, rop]];
    628             }
    629         }
    630     }
    631     // System.out.println("rewrites after="+rewrites);
    632     ANTLRHashMap *m = [ANTLRHashMap newANTLRHashMapWithLen:15];
    633     for (int i = 0; i < [rewrites count]; i++) {
    634         ANTLRRewriteOperation *op = (ANTLRRewriteOperation *)[rewrites objectAtIndex:i];
    635         if ( op == nil )
    636             continue; // ignore deleted ops
    637         if ( [m objectAtIndex:op.rwIndex] != nil ) {
    638             @throw [ANTLRRuntimeException newException:@"should only be one op per rwIndex\n"];
    639         }
    640         //[m put(new Integer(op.rwIndex), op);
    641         [m setObject:op atIndex:op.rwIndex];
    642     }
    643     //System.out.println("rwIndex to op: "+m);
    644     if (debug > 1) NSLog(@"rwIndex to  op %d\n", (NSInteger)m);
    645     return m;
    646 }
    647 
    648 - (NSString *)catOpText:(id)a PrevText:(id)b
    649 {
    650     NSString *x = @"";
    651     NSString *y = @"";
    652     if ( a != nil )
    653         x = [a toString];
    654     if ( b != nil )
    655         y = [b toString];
    656     return [NSString stringWithFormat:@"%@%@",x, y];
    657 }
    658 
    659 - (ANTLRHashMap *)getKindOfOps:(ANTLRHashMap *)rewrites KindOfClass:(Class)kind
    660 {
    661     return [self getKindOfOps:rewrites KindOfClass:kind Index:[rewrites count]];
    662 }
    663 
    664 /** Get all operations before an rwIndex of a particular kind */
    665 - (ANTLRHashMap *)getKindOfOps:(ANTLRHashMap *)rewrites KindOfClass:(Class)kind Index:(NSInteger)before
    666 {
    667     ANTLRHashMap *ops = [ANTLRHashMap newANTLRHashMapWithLen:15];
    668     for (int i = 0; i < before && i < [rewrites count]; i++) {
    669         ANTLRRewriteOperation *op = (ANTLRRewriteOperation *)[rewrites objectAtIndex:i];
    670         if ( op == nil )
    671             continue; // ignore deleted
    672         if ( [op isKindOfClass:(Class)kind] )
    673             [ops addObject:op];
    674     }		
    675     return ops;
    676 }
    677 
    678 - (NSMutableString *)toDebugString
    679 {
    680     return [self toDebugStringFromStart:MIN_TOKEN_INDEX ToEnd:[tokens count]-1];
    681 }
    682 
    683 - (NSMutableString *)toDebugStringFromStart:(NSInteger)start ToEnd:(NSInteger)end
    684 {
    685     NSMutableString *buf = [NSMutableString stringWithCapacity:100];
    686     for (int i = start; i >= MIN_TOKEN_INDEX && i <= end && i < [tokens count]; i++) {
    687         [buf appendString:[[tokens objectAtIndex:i] text]];
    688     }
    689     return [NSString stringWithString:buf];
    690 }
    691 
    692 @end
    693