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