1 /* 2 * [The "BSD licence"] 3 * Copyright (c) 2005-2008 Terence Parr 4 * All rights reserved. 5 * 6 * Conversion to C#: 7 * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc. 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 33 namespace Antlr.Runtime 34 { 35 using System.Collections.Generic; 36 37 using ArgumentException = System.ArgumentException; 38 using Console = System.Console; 39 using Math = System.Math; 40 using DebuggerDisplay = System.Diagnostics.DebuggerDisplayAttribute; 41 using Exception = System.Exception; 42 using StringBuilder = System.Text.StringBuilder; 43 using Type = System.Type; 44 45 /** Useful for dumping out the input stream after doing some 46 * augmentation or other manipulations. 47 * 48 * You can insert stuff, replace, and delete chunks. Note that the 49 * operations are done lazily--only if you convert the buffer to a 50 * String. This is very efficient because you are not moving data around 51 * all the time. As the buffer of tokens is converted to strings, the 52 * toString() method(s) check to see if there is an operation at the 53 * current index. If so, the operation is done and then normal String 54 * rendering continues on the buffer. This is like having multiple Turing 55 * machine instruction streams (programs) operating on a single input tape. :) 56 * 57 * Since the operations are done lazily at toString-time, operations do not 58 * screw up the token index values. That is, an insert operation at token 59 * index i does not change the index values for tokens i+1..n-1. 60 * 61 * Because operations never actually alter the buffer, you may always get 62 * the original token stream back without undoing anything. Since 63 * the instructions are queued up, you can easily simulate transactions and 64 * roll back any changes if there is an error just by removing instructions. 65 * For example, 66 * 67 * CharStream input = new ANTLRFileStream("input"); 68 * TLexer lex = new TLexer(input); 69 * TokenRewriteStream tokens = new TokenRewriteStream(lex); 70 * T parser = new T(tokens); 71 * parser.startRule(); 72 * 73 * Then in the rules, you can execute 74 * Token t,u; 75 * ... 76 * input.insertAfter(t, "text to put after t");} 77 * input.insertAfter(u, "text after u");} 78 * System.out.println(tokens.toString()); 79 * 80 * Actually, you have to cast the 'input' to a TokenRewriteStream. :( 81 * 82 * You can also have multiple "instruction streams" and get multiple 83 * rewrites from a single pass over the input. Just name the instruction 84 * streams and use that name again when printing the buffer. This could be 85 * useful for generating a C file and also its header file--all from the 86 * same buffer: 87 * 88 * tokens.insertAfter("pass1", t, "text to put after t");} 89 * tokens.insertAfter("pass2", u, "text after u");} 90 * System.out.println(tokens.toString("pass1")); 91 * System.out.println(tokens.toString("pass2")); 92 * 93 * If you don't use named rewrite streams, a "default" stream is used as 94 * the first example shows. 95 */ 96 [System.Serializable] 97 [DebuggerDisplay( "TODO: TokenRewriteStream debugger display" )] 98 public class TokenRewriteStream : CommonTokenStream 99 { 100 public const string DEFAULT_PROGRAM_NAME = "default"; 101 public const int PROGRAM_INIT_SIZE = 100; 102 public const int MIN_TOKEN_INDEX = 0; 103 104 // Define the rewrite operation hierarchy 105 106 protected class RewriteOperation 107 { 108 /** <summary>What index into rewrites List are we?</summary> */ 109 public int instructionIndex; 110 /** <summary>Token buffer index.</summary> */ 111 public int index; 112 public object text; 113 // outer 114 protected TokenRewriteStream stream; 115 RewriteOperation(TokenRewriteStream stream, int index)116 protected RewriteOperation(TokenRewriteStream stream, int index) 117 { 118 this.stream = stream; 119 this.index = index; 120 } 121 RewriteOperation( TokenRewriteStream stream, int index, object text )122 protected RewriteOperation( TokenRewriteStream stream, int index, object text ) 123 { 124 this.index = index; 125 this.text = text; 126 this.stream = stream; 127 } 128 129 /** <summary> 130 * Execute the rewrite operation by possibly adding to the buffer. 131 * Return the index of the next token to operate on. 132 * </summary> 133 */ Execute( StringBuilder buf )134 public virtual int Execute( StringBuilder buf ) 135 { 136 return index; 137 } 138 ToString()139 public override string ToString() 140 { 141 string opName = this.GetType().Name; 142 int dindex = opName.IndexOf( '$' ); 143 opName = opName.Substring( dindex + 1 ); 144 return string.Format("<{0}@{1}:\"{2}\">", opName, stream._tokens[index], text); 145 } 146 } 147 148 private class InsertBeforeOp : RewriteOperation 149 { InsertBeforeOp( TokenRewriteStream stream, int index, object text )150 public InsertBeforeOp( TokenRewriteStream stream, int index, object text ) : 151 base( stream, index, text ) 152 { 153 } 154 Execute( StringBuilder buf )155 public override int Execute( StringBuilder buf ) 156 { 157 buf.Append( text ); 158 if (stream._tokens[index].Type != CharStreamConstants.EndOfFile) 159 buf.Append(stream._tokens[index].Text); 160 return index + 1; 161 } 162 } 163 164 /** <summary> 165 * I'm going to try replacing range from x..y with (y-x)+1 ReplaceOp 166 * instructions. 167 * </summary> 168 */ 169 private class ReplaceOp : RewriteOperation 170 { 171 public int lastIndex; ReplaceOp( TokenRewriteStream stream, int from, int to, object text )172 public ReplaceOp( TokenRewriteStream stream, int from, int to, object text ) 173 : base( stream, from, text ) 174 { 175 lastIndex = to; 176 } 177 Execute( StringBuilder buf )178 public override int Execute( StringBuilder buf ) 179 { 180 if ( text != null ) 181 { 182 buf.Append( text ); 183 } 184 return lastIndex + 1; 185 } 186 ToString()187 public override string ToString() 188 { 189 if (text == null) 190 { 191 return string.Format("<DeleteOp@{0}..{1}>", stream._tokens[index], stream._tokens[lastIndex]); 192 } 193 194 return string.Format("<ReplaceOp@{0}..{1}:\"{2}\">", stream._tokens[index], stream._tokens[lastIndex], text); 195 } 196 } 197 198 /** <summary> 199 * You may have multiple, named streams of rewrite operations. 200 * I'm calling these things "programs." 201 * Maps String (name) -> rewrite (List) 202 * </summary> 203 */ 204 protected IDictionary<string, IList<RewriteOperation>> programs = null; 205 206 /** <summary>Map String (program name) -> Integer index</summary> */ 207 protected IDictionary<string, int> lastRewriteTokenIndexes = null; 208 TokenRewriteStream()209 public TokenRewriteStream() 210 { 211 Init(); 212 } 213 Init()214 protected void Init() 215 { 216 programs = new Dictionary<string, IList<RewriteOperation>>(); 217 programs[DEFAULT_PROGRAM_NAME] = new List<RewriteOperation>( PROGRAM_INIT_SIZE ); 218 lastRewriteTokenIndexes = new Dictionary<string, int>(); 219 } 220 TokenRewriteStream( ITokenSource tokenSource )221 public TokenRewriteStream( ITokenSource tokenSource ) 222 : base( tokenSource ) 223 { 224 Init(); 225 } 226 TokenRewriteStream( ITokenSource tokenSource, int channel )227 public TokenRewriteStream( ITokenSource tokenSource, int channel ) 228 : base( tokenSource, channel ) 229 { 230 Init(); 231 } 232 Rollback( int instructionIndex )233 public virtual void Rollback( int instructionIndex ) 234 { 235 Rollback( DEFAULT_PROGRAM_NAME, instructionIndex ); 236 } 237 238 /** <summary> 239 * Rollback the instruction stream for a program so that 240 * the indicated instruction (via instructionIndex) is no 241 * longer in the stream. UNTESTED! 242 * </summary> 243 */ Rollback( string programName, int instructionIndex )244 public virtual void Rollback( string programName, int instructionIndex ) 245 { 246 IList<RewriteOperation> @is; 247 if ( programs.TryGetValue( programName, out @is ) && @is != null ) 248 { 249 List<RewriteOperation> sublist = new List<RewriteOperation>(); 250 for ( int i = MIN_TOKEN_INDEX; i <= instructionIndex; i++ ) 251 sublist.Add( @is[i] ); 252 253 programs[programName] = sublist; 254 } 255 } 256 DeleteProgram()257 public virtual void DeleteProgram() 258 { 259 DeleteProgram( DEFAULT_PROGRAM_NAME ); 260 } 261 262 /** <summary>Reset the program so that no instructions exist</summary> */ DeleteProgram( string programName )263 public virtual void DeleteProgram( string programName ) 264 { 265 Rollback( programName, MIN_TOKEN_INDEX ); 266 } 267 InsertAfter( IToken t, object text )268 public virtual void InsertAfter( IToken t, object text ) 269 { 270 InsertAfter( DEFAULT_PROGRAM_NAME, t, text ); 271 } 272 InsertAfter( int index, object text )273 public virtual void InsertAfter( int index, object text ) 274 { 275 InsertAfter( DEFAULT_PROGRAM_NAME, index, text ); 276 } 277 InsertAfter( string programName, IToken t, object text )278 public virtual void InsertAfter( string programName, IToken t, object text ) 279 { 280 InsertAfter( programName, t.TokenIndex, text ); 281 } 282 InsertAfter( string programName, int index, object text )283 public virtual void InsertAfter( string programName, int index, object text ) 284 { 285 // to insert after, just insert before next index (even if past end) 286 InsertBefore( programName, index + 1, text ); 287 } 288 InsertBefore( IToken t, object text )289 public virtual void InsertBefore( IToken t, object text ) 290 { 291 InsertBefore( DEFAULT_PROGRAM_NAME, t, text ); 292 } 293 InsertBefore( int index, object text )294 public virtual void InsertBefore( int index, object text ) 295 { 296 InsertBefore( DEFAULT_PROGRAM_NAME, index, text ); 297 } 298 InsertBefore( string programName, IToken t, object text )299 public virtual void InsertBefore( string programName, IToken t, object text ) 300 { 301 InsertBefore( programName, t.TokenIndex, text ); 302 } 303 InsertBefore( string programName, int index, object text )304 public virtual void InsertBefore( string programName, int index, object text ) 305 { 306 RewriteOperation op = new InsertBeforeOp( this, index, text ); 307 IList<RewriteOperation> rewrites = GetProgram( programName ); 308 op.instructionIndex = rewrites.Count; 309 rewrites.Add( op ); 310 } 311 Replace( int index, object text )312 public virtual void Replace( int index, object text ) 313 { 314 Replace( DEFAULT_PROGRAM_NAME, index, index, text ); 315 } 316 Replace( int from, int to, object text )317 public virtual void Replace( int from, int to, object text ) 318 { 319 Replace( DEFAULT_PROGRAM_NAME, from, to, text ); 320 } 321 Replace( IToken indexT, object text )322 public virtual void Replace( IToken indexT, object text ) 323 { 324 Replace( DEFAULT_PROGRAM_NAME, indexT, indexT, text ); 325 } 326 Replace( IToken from, IToken to, object text )327 public virtual void Replace( IToken from, IToken to, object text ) 328 { 329 Replace( DEFAULT_PROGRAM_NAME, from, to, text ); 330 } 331 Replace( string programName, int from, int to, object text )332 public virtual void Replace( string programName, int from, int to, object text ) 333 { 334 if ( from > to || from < 0 || to < 0 || to >= _tokens.Count ) 335 { 336 throw new ArgumentException( "replace: range invalid: " + from + ".." + to + "(size=" + _tokens.Count + ")" ); 337 } 338 RewriteOperation op = new ReplaceOp( this, from, to, text ); 339 IList<RewriteOperation> rewrites = GetProgram( programName ); 340 op.instructionIndex = rewrites.Count; 341 rewrites.Add( op ); 342 } 343 Replace( string programName, IToken from, IToken to, object text )344 public virtual void Replace( string programName, IToken from, IToken to, object text ) 345 { 346 Replace( programName, 347 from.TokenIndex, 348 to.TokenIndex, 349 text ); 350 } 351 Delete( int index )352 public virtual void Delete( int index ) 353 { 354 Delete( DEFAULT_PROGRAM_NAME, index, index ); 355 } 356 Delete( int from, int to )357 public virtual void Delete( int from, int to ) 358 { 359 Delete( DEFAULT_PROGRAM_NAME, from, to ); 360 } 361 Delete( IToken indexT )362 public virtual void Delete( IToken indexT ) 363 { 364 Delete( DEFAULT_PROGRAM_NAME, indexT, indexT ); 365 } 366 Delete( IToken from, IToken to )367 public virtual void Delete( IToken from, IToken to ) 368 { 369 Delete( DEFAULT_PROGRAM_NAME, from, to ); 370 } 371 Delete( string programName, int from, int to )372 public virtual void Delete( string programName, int from, int to ) 373 { 374 Replace( programName, from, to, null ); 375 } 376 Delete( string programName, IToken from, IToken to )377 public virtual void Delete( string programName, IToken from, IToken to ) 378 { 379 Replace( programName, from, to, null ); 380 } 381 GetLastRewriteTokenIndex()382 public virtual int GetLastRewriteTokenIndex() 383 { 384 return GetLastRewriteTokenIndex( DEFAULT_PROGRAM_NAME ); 385 } 386 GetLastRewriteTokenIndex( string programName )387 protected virtual int GetLastRewriteTokenIndex( string programName ) 388 { 389 int value; 390 if ( lastRewriteTokenIndexes.TryGetValue( programName, out value ) ) 391 return value; 392 393 return -1; 394 } 395 SetLastRewriteTokenIndex( string programName, int i )396 protected virtual void SetLastRewriteTokenIndex( string programName, int i ) 397 { 398 lastRewriteTokenIndexes[programName] = i; 399 } 400 GetProgram( string name )401 protected virtual IList<RewriteOperation> GetProgram( string name ) 402 { 403 IList<RewriteOperation> @is; 404 if ( !programs.TryGetValue( name, out @is ) || @is == null ) 405 { 406 @is = InitializeProgram( name ); 407 } 408 return @is; 409 } 410 InitializeProgram( string name )411 private IList<RewriteOperation> InitializeProgram( string name ) 412 { 413 IList<RewriteOperation> @is = new List<RewriteOperation>( PROGRAM_INIT_SIZE ); 414 programs[name] = @is; 415 return @is; 416 } 417 ToOriginalString()418 public virtual string ToOriginalString() 419 { 420 Fill(); 421 return ToOriginalString( MIN_TOKEN_INDEX, Count - 1 ); 422 } 423 ToOriginalString( int start, int end )424 public virtual string ToOriginalString( int start, int end ) 425 { 426 StringBuilder buf = new StringBuilder(); 427 for ( int i = start; i >= MIN_TOKEN_INDEX && i <= end && i < _tokens.Count; i++ ) 428 { 429 if (Get(i).Type != CharStreamConstants.EndOfFile) 430 buf.Append(Get(i).Text); 431 } 432 return buf.ToString(); 433 } 434 ToString()435 public override string ToString() 436 { 437 Fill(); 438 return ToString( MIN_TOKEN_INDEX, Count - 1 ); 439 } 440 ToString( string programName )441 public virtual string ToString( string programName ) 442 { 443 Fill(); 444 return ToString(programName, MIN_TOKEN_INDEX, Count - 1); 445 } 446 ToString( int start, int end )447 public override string ToString( int start, int end ) 448 { 449 return ToString( DEFAULT_PROGRAM_NAME, start, end ); 450 } 451 ToString( string programName, int start, int end )452 public virtual string ToString( string programName, int start, int end ) 453 { 454 IList<RewriteOperation> rewrites; 455 if ( !programs.TryGetValue( programName, out rewrites ) ) 456 rewrites = null; 457 458 // ensure start/end are in range 459 if ( end > _tokens.Count - 1 ) 460 end = _tokens.Count - 1; 461 if ( start < 0 ) 462 start = 0; 463 464 if ( rewrites == null || rewrites.Count == 0 ) 465 { 466 return ToOriginalString( start, end ); // no instructions to execute 467 } 468 StringBuilder buf = new StringBuilder(); 469 470 // First, optimize instruction stream 471 IDictionary<int, RewriteOperation> indexToOp = ReduceToSingleOperationPerIndex( rewrites ); 472 473 // Walk buffer, executing instructions and emitting tokens 474 int i = start; 475 while ( i <= end && i < _tokens.Count ) 476 { 477 RewriteOperation op; 478 bool exists = indexToOp.TryGetValue( i, out op ); 479 480 if ( exists ) 481 { 482 // remove so any left have index size-1 483 indexToOp.Remove( i ); 484 } 485 486 if ( !exists || op == null ) 487 { 488 IToken t = _tokens[i]; 489 // no operation at that index, just dump token 490 if (t.Type != CharStreamConstants.EndOfFile) 491 buf.Append(t.Text); 492 i++; // move to next token 493 } 494 else 495 { 496 i = op.Execute( buf ); // execute operation and skip 497 } 498 } 499 500 // include stuff after end if it's last index in buffer 501 // So, if they did an insertAfter(lastValidIndex, "foo"), include 502 // foo if end==lastValidIndex. 503 if ( end == _tokens.Count - 1 ) 504 { 505 // Scan any remaining operations after last token 506 // should be included (they will be inserts). 507 foreach ( RewriteOperation op in indexToOp.Values ) 508 { 509 if ( op.index >= _tokens.Count - 1 ) 510 buf.Append( op.text ); 511 } 512 } 513 return buf.ToString(); 514 } 515 516 /** We need to combine operations and report invalid operations (like 517 * overlapping replaces that are not completed nested). Inserts to 518 * same index need to be combined etc... Here are the cases: 519 * 520 * I.i.u I.j.v leave alone, nonoverlapping 521 * I.i.u I.i.v combine: Iivu 522 * 523 * R.i-j.u R.x-y.v | i-j in x-y delete first R 524 * R.i-j.u R.i-j.v delete first R 525 * R.i-j.u R.x-y.v | x-y in i-j ERROR 526 * R.i-j.u R.x-y.v | boundaries overlap ERROR 527 * 528 * Delete special case of replace (text==null): 529 * D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right) 530 * 531 * I.i.u R.x-y.v | i in (x+1)-y delete I (since insert before 532 * we're not deleting i) 533 * I.i.u R.x-y.v | i not in (x+1)-y leave alone, nonoverlapping 534 * R.x-y.v I.i.u | i in x-y ERROR 535 * R.x-y.v I.x.u R.x-y.uv (combine, delete I) 536 * R.x-y.v I.i.u | i not in x-y leave alone, nonoverlapping 537 * 538 * I.i.u = insert u before op @ index i 539 * R.x-y.u = replace x-y indexed tokens with u 540 * 541 * First we need to examine replaces. For any replace op: 542 * 543 * 1. wipe out any insertions before op within that range. 544 * 2. Drop any replace op before that is contained completely within 545 * that range. 546 * 3. Throw exception upon boundary overlap with any previous replace. 547 * 548 * Then we can deal with inserts: 549 * 550 * 1. for any inserts to same index, combine even if not adjacent. 551 * 2. for any prior replace with same left boundary, combine this 552 * insert with replace and delete this replace. 553 * 3. throw exception if index in same range as previous replace 554 * 555 * Don't actually delete; make op null in list. Easier to walk list. 556 * Later we can throw as we add to index -> op map. 557 * 558 * Note that I.2 R.2-2 will wipe out I.2 even though, technically, the 559 * inserted stuff would be before the replace range. But, if you 560 * add tokens in front of a method body '{' and then delete the method 561 * body, I think the stuff before the '{' you added should disappear too. 562 * 563 * Return a map from token index to operation. 564 */ ReduceToSingleOperationPerIndex( IList<RewriteOperation> rewrites )565 protected virtual IDictionary<int, RewriteOperation> ReduceToSingleOperationPerIndex( IList<RewriteOperation> rewrites ) 566 { 567 //System.out.println("rewrites="+rewrites); 568 569 // WALK REPLACES 570 for ( int i = 0; i < rewrites.Count; i++ ) 571 { 572 RewriteOperation op = rewrites[i]; 573 if ( op == null ) 574 continue; 575 if ( !( op is ReplaceOp ) ) 576 continue; 577 ReplaceOp rop = (ReplaceOp)rewrites[i]; 578 // Wipe prior inserts within range 579 var inserts = GetKindOfOps( rewrites, typeof( InsertBeforeOp ), i ); 580 for ( int j = 0; j < inserts.Count; j++ ) 581 { 582 InsertBeforeOp iop = (InsertBeforeOp)inserts[j]; 583 if (iop.index == rop.index) 584 { 585 // E.g., insert before 2, delete 2..2; update replace 586 // text to include insert before, kill insert 587 rewrites[iop.instructionIndex] = null; 588 rop.text = iop.text.ToString() + (rop.text != null ? rop.text.ToString() : string.Empty); 589 } 590 else if (iop.index > rop.index && iop.index <= rop.lastIndex) 591 { 592 // delete insert as it's a no-op. 593 rewrites[iop.instructionIndex] = null; 594 } 595 } 596 // Drop any prior replaces contained within 597 var prevReplaces = GetKindOfOps( rewrites, typeof( ReplaceOp ), i ); 598 for ( int j = 0; j < prevReplaces.Count; j++ ) 599 { 600 ReplaceOp prevRop = (ReplaceOp)prevReplaces[j]; 601 if ( prevRop.index >= rop.index && prevRop.lastIndex <= rop.lastIndex ) 602 { 603 // delete replace as it's a no-op. 604 rewrites[prevRop.instructionIndex] = null; 605 continue; 606 } 607 // throw exception unless disjoint or identical 608 bool disjoint = 609 prevRop.lastIndex < rop.index || prevRop.index > rop.lastIndex; 610 bool same = 611 prevRop.index == rop.index && prevRop.lastIndex == rop.lastIndex; 612 // Delete special case of replace (text==null): 613 // D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right) 614 if (prevRop.text == null && rop.text == null && !disjoint) 615 { 616 //System.out.println("overlapping deletes: "+prevRop+", "+rop); 617 rewrites[prevRop.instructionIndex] = null; // kill first delete 618 rop.index = Math.Min(prevRop.index, rop.index); 619 rop.lastIndex = Math.Max(prevRop.lastIndex, rop.lastIndex); 620 Console.WriteLine("new rop " + rop); 621 } 622 else if ( !disjoint && !same ) 623 { 624 throw new ArgumentException( "replace op boundaries of " + rop + 625 " overlap with previous " + prevRop ); 626 } 627 } 628 } 629 630 // WALK INSERTS 631 for ( int i = 0; i < rewrites.Count; i++ ) 632 { 633 RewriteOperation op = (RewriteOperation)rewrites[i]; 634 if ( op == null ) 635 continue; 636 if ( !( op is InsertBeforeOp ) ) 637 continue; 638 InsertBeforeOp iop = (InsertBeforeOp)rewrites[i]; 639 // combine current insert with prior if any at same index 640 var prevInserts = GetKindOfOps( rewrites, typeof( InsertBeforeOp ), i ); 641 for ( int j = 0; j < prevInserts.Count; j++ ) 642 { 643 InsertBeforeOp prevIop = (InsertBeforeOp)prevInserts[j]; 644 if ( prevIop.index == iop.index ) 645 { // combine objects 646 // convert to strings...we're in process of toString'ing 647 // whole token buffer so no lazy eval issue with any templates 648 iop.text = CatOpText( iop.text, prevIop.text ); 649 // delete redundant prior insert 650 rewrites[prevIop.instructionIndex] = null; 651 } 652 } 653 // look for replaces where iop.index is in range; error 654 var prevReplaces = GetKindOfOps( rewrites, typeof( ReplaceOp ), i ); 655 for ( int j = 0; j < prevReplaces.Count; j++ ) 656 { 657 ReplaceOp rop = (ReplaceOp)prevReplaces[j]; 658 if ( iop.index == rop.index ) 659 { 660 rop.text = CatOpText( iop.text, rop.text ); 661 rewrites[i] = null; // delete current insert 662 continue; 663 } 664 if ( iop.index >= rop.index && iop.index <= rop.lastIndex ) 665 { 666 throw new ArgumentException( "insert op " + iop + 667 " within boundaries of previous " + rop ); 668 } 669 } 670 } 671 // System.out.println("rewrites after="+rewrites); 672 IDictionary<int, RewriteOperation> m = new Dictionary<int, RewriteOperation>(); 673 for ( int i = 0; i < rewrites.Count; i++ ) 674 { 675 RewriteOperation op = (RewriteOperation)rewrites[i]; 676 if ( op == null ) 677 continue; // ignore deleted ops 678 679 RewriteOperation existing; 680 if ( m.TryGetValue( op.index, out existing ) && existing != null ) 681 { 682 throw new Exception( "should only be one op per index" ); 683 } 684 m[op.index] = op; 685 } 686 //System.out.println("index to op: "+m); 687 return m; 688 } 689 CatOpText( object a, object b )690 protected virtual string CatOpText( object a, object b ) 691 { 692 return string.Concat( a, b ); 693 } GetKindOfOps( IList<RewriteOperation> rewrites, Type kind )694 protected virtual IList<RewriteOperation> GetKindOfOps( IList<RewriteOperation> rewrites, Type kind ) 695 { 696 return GetKindOfOps( rewrites, kind, rewrites.Count ); 697 } 698 699 /** <summary>Get all operations before an index of a particular kind</summary> */ GetKindOfOps( IList<RewriteOperation> rewrites, Type kind, int before )700 protected virtual IList<RewriteOperation> GetKindOfOps( IList<RewriteOperation> rewrites, Type kind, int before ) 701 { 702 IList<RewriteOperation> ops = new List<RewriteOperation>(); 703 for ( int i = 0; i < before && i < rewrites.Count; i++ ) 704 { 705 RewriteOperation op = rewrites[i]; 706 if ( op == null ) 707 continue; // ignore deleted 708 if ( op.GetType() == kind ) 709 ops.Add( op ); 710 } 711 return ops; 712 } 713 ToDebugString()714 public virtual string ToDebugString() 715 { 716 return ToDebugString( MIN_TOKEN_INDEX, Count - 1 ); 717 } 718 ToDebugString( int start, int end )719 public virtual string ToDebugString( int start, int end ) 720 { 721 StringBuilder buf = new StringBuilder(); 722 for ( int i = start; i >= MIN_TOKEN_INDEX && i <= end && i < _tokens.Count; i++ ) 723 { 724 buf.Append( Get( i ) ); 725 } 726 return buf.ToString(); 727 } 728 } 729 } 730