19e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner/* 29e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * [The "BSD licence"] 39e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * Copyright (c) 2005-2008 Terence Parr 49e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * All rights reserved. 53060910e290949a9ac5eda8726d030790c4d60ffChris Lattner * 63060910e290949a9ac5eda8726d030790c4d60ffChris Lattner * Conversion to C#: 79e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc. 89e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * All rights reserved. 99e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * 109e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * Redistribution and use in source and binary forms, with or without 119e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * modification, are permitted provided that the following conditions 129e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * are met: 139e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * 1. Redistributions of source code must retain the above copyright 1437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * notice, this list of conditions and the following disclaimer. 1537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * 2. Redistributions in binary form must reproduce the above copyright 169e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * notice, this list of conditions and the following disclaimer in the 1736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * documentation and/or other materials provided with the distribution. 189e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * 3. The name of the author may not be used to endorse or promote products 199e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * derived from this software without specific prior written permission. 209e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * 219e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 229e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 239e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 248850a1bcef0c2a785f918395fe0a05054914b349Chris Lattner * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 259e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 269e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27c4de3dec62c3f60ae7297f93c19c799c403c2e9fReid Spencer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 289e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 299e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30022f64fbbc4669623e79b805379266fed519017dChris Lattner * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen Hines */ 323f8b8913bc9cb232871445eefa8654caf7f9986fChris Lattner 3383ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sandsnamespace Antlr.Runtime 34cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling{ 35cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling using System.Collections.Generic; 36cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling 37cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling using ArgumentException = System.ArgumentException; 38da4231f134989af7dc6bd3408821ba573def27b2Jim Grosbach using Console = System.Console; 39cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling using Math = System.Math; 40cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling using DebuggerDisplay = System.Diagnostics.DebuggerDisplayAttribute; 41825b72b0571821bf2d378749f69d6c4cfb52d2f9Owen Anderson using Exception = System.Exception; 42cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling using StringBuilder = System.Text.StringBuilder; 43cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling using Type = System.Type; 44cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling 45825b72b0571821bf2d378749f69d6c4cfb52d2f9Owen Anderson /** Useful for dumping out the input stream after doing some 46cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * augmentation or other manipulations. 47cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * 48cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * You can insert stuff, replace, and delete chunks. Note that the 49cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * operations are done lazily--only if you convert the buffer to a 50825b72b0571821bf2d378749f69d6c4cfb52d2f9Owen Anderson * String. This is very efficient because you are not moving data around 51cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * all the time. As the buffer of tokens is converted to strings, the 52cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * toString() method(s) check to see if there is an operation at the 53cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * current index. If so, the operation is done and then normal String 54825b72b0571821bf2d378749f69d6c4cfb52d2f9Owen Anderson * rendering continues on the buffer. This is like having multiple Turing 55cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * machine instruction streams (programs) operating on a single input tape. :) 56cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * 57cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * Since the operations are done lazily at toString-time, operations do not 58cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * screw up the token index values. That is, an insert operation at token 59cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * index i does not change the index values for tokens i+1..n-1. 60cdcc3e6e12b8b4e224bd62c96768c5f5e325aaceBill Wendling * 6183ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands * Because operations never actually alter the buffer, you may always get 629e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * the original token stream back without undoing anything. Since 63cddc3e03e4ec99c0268c03a126195173e519ed58Pirama Arumuga Nainar * the instructions are queued up, you can easily simulate transactions and 647365c091f92db5e68c98d7faedc6c34e1bbbc898Dan Gohman * roll back any changes if there is an error just by removing instructions. 65cddc3e03e4ec99c0268c03a126195173e519ed58Pirama Arumuga Nainar * For example, 66cddc3e03e4ec99c0268c03a126195173e519ed58Pirama Arumuga Nainar * 679e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner * CharStream input = new ANTLRFileStream("input"); 68a62c302ddd79c525d6fac050974911d36662ebfeChris Lattner * TLexer lex = new TLexer(input); 69a62c302ddd79c525d6fac050974911d36662ebfeChris Lattner * TokenRewriteStream tokens = new TokenRewriteStream(lex); 70c4de3dec62c3f60ae7297f93c19c799c403c2e9fReid Spencer * T parser = new T(tokens); 71c4de3dec62c3f60ae7297f93c19c799c403c2e9fReid Spencer * parser.startRule(); 72a62c302ddd79c525d6fac050974911d36662ebfeChris Lattner * 736bd9567a6a1ba62118cdd258ddc52ea8f82ff511Evan Cheng * Then in the rules, you can execute 74bd0fa4c00d7870b1da36eac7b2181700381f2f96John McCall * Token t,u; 75bd0fa4c00d7870b1da36eac7b2181700381f2f96John McCall * ... 76bd0fa4c00d7870b1da36eac7b2181700381f2f96John McCall * input.insertAfter(t, "text to put after t");} 7786208903cb3b693b26e144b8c5c7a0ab6a9a45c6Chris Lattner * input.insertAfter(u, "text after u");} 7836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * System.out.println(tokens.toString()); 7936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * 8036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * Actually, you have to cast the 'input' to a TokenRewriteStream. :( 8186208903cb3b693b26e144b8c5c7a0ab6a9a45c6Chris Lattner * 8286208903cb3b693b26e144b8c5c7a0ab6a9a45c6Chris Lattner * You can also have multiple "instruction streams" and get multiple 8386208903cb3b693b26e144b8c5c7a0ab6a9a45c6Chris Lattner * rewrites from a single pass over the input. Just name the instruction 846948897e478cbd66626159776a8017b3c18579b9Pirama Arumuga Nainar * streams and use that name again when printing the buffer. This could be 856948897e478cbd66626159776a8017b3c18579b9Pirama Arumuga Nainar * useful for generating a C file and also its header file--all from the 866948897e478cbd66626159776a8017b3c18579b9Pirama Arumuga Nainar * same buffer: 87a62c302ddd79c525d6fac050974911d36662ebfeChris Lattner * 88dc89737bcdbb8f69d8ae7578bdfa904cabcfc5edNick Lewycky * tokens.insertAfter("pass1", t, "text to put after t");} 89dc89737bcdbb8f69d8ae7578bdfa904cabcfc5edNick Lewycky * tokens.insertAfter("pass2", u, "text after u");} 90dc89737bcdbb8f69d8ae7578bdfa904cabcfc5edNick Lewycky * System.out.println(tokens.toString("pass1")); 91a62c302ddd79c525d6fac050974911d36662ebfeChris Lattner * System.out.println(tokens.toString("pass2")); 92a62c302ddd79c525d6fac050974911d36662ebfeChris Lattner * 936bd9567a6a1ba62118cdd258ddc52ea8f82ff511Evan Cheng * If you don't use named rewrite streams, a "default" stream is used as 94ee4fa1977dd3a495a8857eef924ee5961db765c6Dan Gohman * the first example shows. 959e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner */ 969e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner [System.Serializable] 979e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner [DebuggerDisplay( "TODO: TokenRewriteStream debugger display" )] 989e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner public class TokenRewriteStream : CommonTokenStream 9949de98214b82fefeb8f16efbf8cdd8813a85469bDale Johannesen { 10049de98214b82fefeb8f16efbf8cdd8813a85469bDale Johannesen public const string DEFAULT_PROGRAM_NAME = "default"; 1019e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner public const int PROGRAM_INIT_SIZE = 100; 1029e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner public const int MIN_TOKEN_INDEX = 0; 1039e493cfcc32aee58e6750ce1efa52d5c3bc3f893Chris Lattner 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 116 protected RewriteOperation(TokenRewriteStream stream, int index) 117 { 118 this.stream = stream; 119 this.index = index; 120 } 121 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 */ 134 public virtual int Execute( StringBuilder buf ) 135 { 136 return index; 137 } 138 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 { 150 public InsertBeforeOp( TokenRewriteStream stream, int index, object text ) : 151 base( stream, index, text ) 152 { 153 } 154 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; 172 public ReplaceOp( TokenRewriteStream stream, int from, int to, object text ) 173 : base( stream, from, text ) 174 { 175 lastIndex = to; 176 } 177 178 public override int Execute( StringBuilder buf ) 179 { 180 if ( text != null ) 181 { 182 buf.Append( text ); 183 } 184 return lastIndex + 1; 185 } 186 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 209 public TokenRewriteStream() 210 { 211 Init(); 212 } 213 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 221 public TokenRewriteStream( ITokenSource tokenSource ) 222 : base( tokenSource ) 223 { 224 Init(); 225 } 226 227 public TokenRewriteStream( ITokenSource tokenSource, int channel ) 228 : base( tokenSource, channel ) 229 { 230 Init(); 231 } 232 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 */ 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 257 public virtual void DeleteProgram() 258 { 259 DeleteProgram( DEFAULT_PROGRAM_NAME ); 260 } 261 262 /** <summary>Reset the program so that no instructions exist</summary> */ 263 public virtual void DeleteProgram( string programName ) 264 { 265 Rollback( programName, MIN_TOKEN_INDEX ); 266 } 267 268 public virtual void InsertAfter( IToken t, object text ) 269 { 270 InsertAfter( DEFAULT_PROGRAM_NAME, t, text ); 271 } 272 273 public virtual void InsertAfter( int index, object text ) 274 { 275 InsertAfter( DEFAULT_PROGRAM_NAME, index, text ); 276 } 277 278 public virtual void InsertAfter( string programName, IToken t, object text ) 279 { 280 InsertAfter( programName, t.TokenIndex, text ); 281 } 282 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 289 public virtual void InsertBefore( IToken t, object text ) 290 { 291 InsertBefore( DEFAULT_PROGRAM_NAME, t, text ); 292 } 293 294 public virtual void InsertBefore( int index, object text ) 295 { 296 InsertBefore( DEFAULT_PROGRAM_NAME, index, text ); 297 } 298 299 public virtual void InsertBefore( string programName, IToken t, object text ) 300 { 301 InsertBefore( programName, t.TokenIndex, text ); 302 } 303 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 312 public virtual void Replace( int index, object text ) 313 { 314 Replace( DEFAULT_PROGRAM_NAME, index, index, text ); 315 } 316 317 public virtual void Replace( int from, int to, object text ) 318 { 319 Replace( DEFAULT_PROGRAM_NAME, from, to, text ); 320 } 321 322 public virtual void Replace( IToken indexT, object text ) 323 { 324 Replace( DEFAULT_PROGRAM_NAME, indexT, indexT, text ); 325 } 326 327 public virtual void Replace( IToken from, IToken to, object text ) 328 { 329 Replace( DEFAULT_PROGRAM_NAME, from, to, text ); 330 } 331 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 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 352 public virtual void Delete( int index ) 353 { 354 Delete( DEFAULT_PROGRAM_NAME, index, index ); 355 } 356 357 public virtual void Delete( int from, int to ) 358 { 359 Delete( DEFAULT_PROGRAM_NAME, from, to ); 360 } 361 362 public virtual void Delete( IToken indexT ) 363 { 364 Delete( DEFAULT_PROGRAM_NAME, indexT, indexT ); 365 } 366 367 public virtual void Delete( IToken from, IToken to ) 368 { 369 Delete( DEFAULT_PROGRAM_NAME, from, to ); 370 } 371 372 public virtual void Delete( string programName, int from, int to ) 373 { 374 Replace( programName, from, to, null ); 375 } 376 377 public virtual void Delete( string programName, IToken from, IToken to ) 378 { 379 Replace( programName, from, to, null ); 380 } 381 382 public virtual int GetLastRewriteTokenIndex() 383 { 384 return GetLastRewriteTokenIndex( DEFAULT_PROGRAM_NAME ); 385 } 386 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 396 protected virtual void SetLastRewriteTokenIndex( string programName, int i ) 397 { 398 lastRewriteTokenIndexes[programName] = i; 399 } 400 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 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 418 public virtual string ToOriginalString() 419 { 420 Fill(); 421 return ToOriginalString( MIN_TOKEN_INDEX, Count - 1 ); 422 } 423 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 435 public override string ToString() 436 { 437 Fill(); 438 return ToString( MIN_TOKEN_INDEX, Count - 1 ); 439 } 440 441 public virtual string ToString( string programName ) 442 { 443 Fill(); 444 return ToString(programName, MIN_TOKEN_INDEX, Count - 1); 445 } 446 447 public override string ToString( int start, int end ) 448 { 449 return ToString( DEFAULT_PROGRAM_NAME, start, end ); 450 } 451 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 */ 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 690 protected virtual string CatOpText( object a, object b ) 691 { 692 return string.Concat( a, b ); 693 } 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> */ 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 714 public virtual string ToDebugString() 715 { 716 return ToDebugString( MIN_TOKEN_INDEX, Count - 1 ); 717 } 718 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