| /* |
| * [The "BSD licence"] |
| * Copyright (c) 2005-2008 Terence Parr |
| * All rights reserved. |
| * |
| * Conversion to C#: |
| * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| namespace Antlr.Runtime |
| { |
| using System.Collections.Generic; |
| |
| using ArgumentException = System.ArgumentException; |
| using Console = System.Console; |
| using Math = System.Math; |
| using DebuggerDisplay = System.Diagnostics.DebuggerDisplayAttribute; |
| using Exception = System.Exception; |
| using StringBuilder = System.Text.StringBuilder; |
| using Type = System.Type; |
| |
| /** Useful for dumping out the input stream after doing some |
| * augmentation or other manipulations. |
| * |
| * You can insert stuff, replace, and delete chunks. Note that the |
| * operations are done lazily--only if you convert the buffer to a |
| * String. This is very efficient because you are not moving data around |
| * all the time. As the buffer of tokens is converted to strings, the |
| * toString() method(s) check to see if there is an operation at the |
| * current index. If so, the operation is done and then normal String |
| * rendering continues on the buffer. This is like having multiple Turing |
| * machine instruction streams (programs) operating on a single input tape. :) |
| * |
| * Since the operations are done lazily at toString-time, operations do not |
| * screw up the token index values. That is, an insert operation at token |
| * index i does not change the index values for tokens i+1..n-1. |
| * |
| * Because operations never actually alter the buffer, you may always get |
| * the original token stream back without undoing anything. Since |
| * the instructions are queued up, you can easily simulate transactions and |
| * roll back any changes if there is an error just by removing instructions. |
| * For example, |
| * |
| * CharStream input = new ANTLRFileStream("input"); |
| * TLexer lex = new TLexer(input); |
| * TokenRewriteStream tokens = new TokenRewriteStream(lex); |
| * T parser = new T(tokens); |
| * parser.startRule(); |
| * |
| * Then in the rules, you can execute |
| * Token t,u; |
| * ... |
| * input.insertAfter(t, "text to put after t");} |
| * input.insertAfter(u, "text after u");} |
| * System.out.println(tokens.toString()); |
| * |
| * Actually, you have to cast the 'input' to a TokenRewriteStream. :( |
| * |
| * You can also have multiple "instruction streams" and get multiple |
| * rewrites from a single pass over the input. Just name the instruction |
| * streams and use that name again when printing the buffer. This could be |
| * useful for generating a C file and also its header file--all from the |
| * same buffer: |
| * |
| * tokens.insertAfter("pass1", t, "text to put after t");} |
| * tokens.insertAfter("pass2", u, "text after u");} |
| * System.out.println(tokens.toString("pass1")); |
| * System.out.println(tokens.toString("pass2")); |
| * |
| * If you don't use named rewrite streams, a "default" stream is used as |
| * the first example shows. |
| */ |
| [System.Serializable] |
| [DebuggerDisplay( "TODO: TokenRewriteStream debugger display" )] |
| public class TokenRewriteStream : CommonTokenStream |
| { |
| public const string DEFAULT_PROGRAM_NAME = "default"; |
| public const int PROGRAM_INIT_SIZE = 100; |
| public const int MIN_TOKEN_INDEX = 0; |
| |
| // Define the rewrite operation hierarchy |
| |
| protected class RewriteOperation |
| { |
| /** <summary>What index into rewrites List are we?</summary> */ |
| public int instructionIndex; |
| /** <summary>Token buffer index.</summary> */ |
| public int index; |
| public object text; |
| // outer |
| protected TokenRewriteStream stream; |
| |
| protected RewriteOperation(TokenRewriteStream stream, int index) |
| { |
| this.stream = stream; |
| this.index = index; |
| } |
| |
| protected RewriteOperation( TokenRewriteStream stream, int index, object text ) |
| { |
| this.index = index; |
| this.text = text; |
| this.stream = stream; |
| } |
| |
| /** <summary> |
| * Execute the rewrite operation by possibly adding to the buffer. |
| * Return the index of the next token to operate on. |
| * </summary> |
| */ |
| public virtual int Execute( StringBuilder buf ) |
| { |
| return index; |
| } |
| |
| public override string ToString() |
| { |
| string opName = this.GetType().Name; |
| int dindex = opName.IndexOf( '$' ); |
| opName = opName.Substring( dindex + 1 ); |
| return string.Format("<{0}@{1}:\"{2}\">", opName, stream._tokens[index], text); |
| } |
| } |
| |
| private class InsertBeforeOp : RewriteOperation |
| { |
| public InsertBeforeOp( TokenRewriteStream stream, int index, object text ) : |
| base( stream, index, text ) |
| { |
| } |
| |
| public override int Execute( StringBuilder buf ) |
| { |
| buf.Append( text ); |
| if (stream._tokens[index].Type != CharStreamConstants.EndOfFile) |
| buf.Append(stream._tokens[index].Text); |
| return index + 1; |
| } |
| } |
| |
| /** <summary> |
| * I'm going to try replacing range from x..y with (y-x)+1 ReplaceOp |
| * instructions. |
| * </summary> |
| */ |
| private class ReplaceOp : RewriteOperation |
| { |
| public int lastIndex; |
| public ReplaceOp( TokenRewriteStream stream, int from, int to, object text ) |
| : base( stream, from, text ) |
| { |
| lastIndex = to; |
| } |
| |
| public override int Execute( StringBuilder buf ) |
| { |
| if ( text != null ) |
| { |
| buf.Append( text ); |
| } |
| return lastIndex + 1; |
| } |
| |
| public override string ToString() |
| { |
| if (text == null) |
| { |
| return string.Format("<DeleteOp@{0}..{1}>", stream._tokens[index], stream._tokens[lastIndex]); |
| } |
| |
| return string.Format("<ReplaceOp@{0}..{1}:\"{2}\">", stream._tokens[index], stream._tokens[lastIndex], text); |
| } |
| } |
| |
| /** <summary> |
| * You may have multiple, named streams of rewrite operations. |
| * I'm calling these things "programs." |
| * Maps String (name) -> rewrite (List) |
| * </summary> |
| */ |
| protected IDictionary<string, IList<RewriteOperation>> programs = null; |
| |
| /** <summary>Map String (program name) -> Integer index</summary> */ |
| protected IDictionary<string, int> lastRewriteTokenIndexes = null; |
| |
| public TokenRewriteStream() |
| { |
| Init(); |
| } |
| |
| protected void Init() |
| { |
| programs = new Dictionary<string, IList<RewriteOperation>>(); |
| programs[DEFAULT_PROGRAM_NAME] = new List<RewriteOperation>( PROGRAM_INIT_SIZE ); |
| lastRewriteTokenIndexes = new Dictionary<string, int>(); |
| } |
| |
| public TokenRewriteStream( ITokenSource tokenSource ) |
| : base( tokenSource ) |
| { |
| Init(); |
| } |
| |
| public TokenRewriteStream( ITokenSource tokenSource, int channel ) |
| : base( tokenSource, channel ) |
| { |
| Init(); |
| } |
| |
| public virtual void Rollback( int instructionIndex ) |
| { |
| Rollback( DEFAULT_PROGRAM_NAME, instructionIndex ); |
| } |
| |
| /** <summary> |
| * Rollback the instruction stream for a program so that |
| * the indicated instruction (via instructionIndex) is no |
| * longer in the stream. UNTESTED! |
| * </summary> |
| */ |
| public virtual void Rollback( string programName, int instructionIndex ) |
| { |
| IList<RewriteOperation> @is; |
| if ( programs.TryGetValue( programName, out @is ) && @is != null ) |
| { |
| List<RewriteOperation> sublist = new List<RewriteOperation>(); |
| for ( int i = MIN_TOKEN_INDEX; i <= instructionIndex; i++ ) |
| sublist.Add( @is[i] ); |
| |
| programs[programName] = sublist; |
| } |
| } |
| |
| public virtual void DeleteProgram() |
| { |
| DeleteProgram( DEFAULT_PROGRAM_NAME ); |
| } |
| |
| /** <summary>Reset the program so that no instructions exist</summary> */ |
| public virtual void DeleteProgram( string programName ) |
| { |
| Rollback( programName, MIN_TOKEN_INDEX ); |
| } |
| |
| public virtual void InsertAfter( IToken t, object text ) |
| { |
| InsertAfter( DEFAULT_PROGRAM_NAME, t, text ); |
| } |
| |
| public virtual void InsertAfter( int index, object text ) |
| { |
| InsertAfter( DEFAULT_PROGRAM_NAME, index, text ); |
| } |
| |
| public virtual void InsertAfter( string programName, IToken t, object text ) |
| { |
| InsertAfter( programName, t.TokenIndex, text ); |
| } |
| |
| public virtual void InsertAfter( string programName, int index, object text ) |
| { |
| // to insert after, just insert before next index (even if past end) |
| InsertBefore( programName, index + 1, text ); |
| } |
| |
| public virtual void InsertBefore( IToken t, object text ) |
| { |
| InsertBefore( DEFAULT_PROGRAM_NAME, t, text ); |
| } |
| |
| public virtual void InsertBefore( int index, object text ) |
| { |
| InsertBefore( DEFAULT_PROGRAM_NAME, index, text ); |
| } |
| |
| public virtual void InsertBefore( string programName, IToken t, object text ) |
| { |
| InsertBefore( programName, t.TokenIndex, text ); |
| } |
| |
| public virtual void InsertBefore( string programName, int index, object text ) |
| { |
| RewriteOperation op = new InsertBeforeOp( this, index, text ); |
| IList<RewriteOperation> rewrites = GetProgram( programName ); |
| op.instructionIndex = rewrites.Count; |
| rewrites.Add( op ); |
| } |
| |
| public virtual void Replace( int index, object text ) |
| { |
| Replace( DEFAULT_PROGRAM_NAME, index, index, text ); |
| } |
| |
| public virtual void Replace( int from, int to, object text ) |
| { |
| Replace( DEFAULT_PROGRAM_NAME, from, to, text ); |
| } |
| |
| public virtual void Replace( IToken indexT, object text ) |
| { |
| Replace( DEFAULT_PROGRAM_NAME, indexT, indexT, text ); |
| } |
| |
| public virtual void Replace( IToken from, IToken to, object text ) |
| { |
| Replace( DEFAULT_PROGRAM_NAME, from, to, text ); |
| } |
| |
| public virtual void Replace( string programName, int from, int to, object text ) |
| { |
| if ( from > to || from < 0 || to < 0 || to >= _tokens.Count ) |
| { |
| throw new ArgumentException( "replace: range invalid: " + from + ".." + to + "(size=" + _tokens.Count + ")" ); |
| } |
| RewriteOperation op = new ReplaceOp( this, from, to, text ); |
| IList<RewriteOperation> rewrites = GetProgram( programName ); |
| op.instructionIndex = rewrites.Count; |
| rewrites.Add( op ); |
| } |
| |
| public virtual void Replace( string programName, IToken from, IToken to, object text ) |
| { |
| Replace( programName, |
| from.TokenIndex, |
| to.TokenIndex, |
| text ); |
| } |
| |
| public virtual void Delete( int index ) |
| { |
| Delete( DEFAULT_PROGRAM_NAME, index, index ); |
| } |
| |
| public virtual void Delete( int from, int to ) |
| { |
| Delete( DEFAULT_PROGRAM_NAME, from, to ); |
| } |
| |
| public virtual void Delete( IToken indexT ) |
| { |
| Delete( DEFAULT_PROGRAM_NAME, indexT, indexT ); |
| } |
| |
| public virtual void Delete( IToken from, IToken to ) |
| { |
| Delete( DEFAULT_PROGRAM_NAME, from, to ); |
| } |
| |
| public virtual void Delete( string programName, int from, int to ) |
| { |
| Replace( programName, from, to, null ); |
| } |
| |
| public virtual void Delete( string programName, IToken from, IToken to ) |
| { |
| Replace( programName, from, to, null ); |
| } |
| |
| public virtual int GetLastRewriteTokenIndex() |
| { |
| return GetLastRewriteTokenIndex( DEFAULT_PROGRAM_NAME ); |
| } |
| |
| protected virtual int GetLastRewriteTokenIndex( string programName ) |
| { |
| int value; |
| if ( lastRewriteTokenIndexes.TryGetValue( programName, out value ) ) |
| return value; |
| |
| return -1; |
| } |
| |
| protected virtual void SetLastRewriteTokenIndex( string programName, int i ) |
| { |
| lastRewriteTokenIndexes[programName] = i; |
| } |
| |
| protected virtual IList<RewriteOperation> GetProgram( string name ) |
| { |
| IList<RewriteOperation> @is; |
| if ( !programs.TryGetValue( name, out @is ) || @is == null ) |
| { |
| @is = InitializeProgram( name ); |
| } |
| return @is; |
| } |
| |
| private IList<RewriteOperation> InitializeProgram( string name ) |
| { |
| IList<RewriteOperation> @is = new List<RewriteOperation>( PROGRAM_INIT_SIZE ); |
| programs[name] = @is; |
| return @is; |
| } |
| |
| public virtual string ToOriginalString() |
| { |
| Fill(); |
| return ToOriginalString( MIN_TOKEN_INDEX, Count - 1 ); |
| } |
| |
| public virtual string ToOriginalString( int start, int end ) |
| { |
| StringBuilder buf = new StringBuilder(); |
| for ( int i = start; i >= MIN_TOKEN_INDEX && i <= end && i < _tokens.Count; i++ ) |
| { |
| if (Get(i).Type != CharStreamConstants.EndOfFile) |
| buf.Append(Get(i).Text); |
| } |
| return buf.ToString(); |
| } |
| |
| public override string ToString() |
| { |
| Fill(); |
| return ToString( MIN_TOKEN_INDEX, Count - 1 ); |
| } |
| |
| public virtual string ToString( string programName ) |
| { |
| Fill(); |
| return ToString(programName, MIN_TOKEN_INDEX, Count - 1); |
| } |
| |
| public override string ToString( int start, int end ) |
| { |
| return ToString( DEFAULT_PROGRAM_NAME, start, end ); |
| } |
| |
| public virtual string ToString( string programName, int start, int end ) |
| { |
| IList<RewriteOperation> rewrites; |
| if ( !programs.TryGetValue( programName, out rewrites ) ) |
| rewrites = null; |
| |
| // ensure start/end are in range |
| if ( end > _tokens.Count - 1 ) |
| end = _tokens.Count - 1; |
| if ( start < 0 ) |
| start = 0; |
| |
| if ( rewrites == null || rewrites.Count == 0 ) |
| { |
| return ToOriginalString( start, end ); // no instructions to execute |
| } |
| StringBuilder buf = new StringBuilder(); |
| |
| // First, optimize instruction stream |
| IDictionary<int, RewriteOperation> indexToOp = ReduceToSingleOperationPerIndex( rewrites ); |
| |
| // Walk buffer, executing instructions and emitting tokens |
| int i = start; |
| while ( i <= end && i < _tokens.Count ) |
| { |
| RewriteOperation op; |
| bool exists = indexToOp.TryGetValue( i, out op ); |
| |
| if ( exists ) |
| { |
| // remove so any left have index size-1 |
| indexToOp.Remove( i ); |
| } |
| |
| if ( !exists || op == null ) |
| { |
| IToken t = _tokens[i]; |
| // no operation at that index, just dump token |
| if (t.Type != CharStreamConstants.EndOfFile) |
| buf.Append(t.Text); |
| i++; // move to next token |
| } |
| else |
| { |
| i = op.Execute( buf ); // execute operation and skip |
| } |
| } |
| |
| // include stuff after end if it's last index in buffer |
| // So, if they did an insertAfter(lastValidIndex, "foo"), include |
| // foo if end==lastValidIndex. |
| if ( end == _tokens.Count - 1 ) |
| { |
| // Scan any remaining operations after last token |
| // should be included (they will be inserts). |
| foreach ( RewriteOperation op in indexToOp.Values ) |
| { |
| if ( op.index >= _tokens.Count - 1 ) |
| buf.Append( op.text ); |
| } |
| } |
| return buf.ToString(); |
| } |
| |
| /** We need to combine operations and report invalid operations (like |
| * overlapping replaces that are not completed nested). Inserts to |
| * same index need to be combined etc... Here are the cases: |
| * |
| * I.i.u I.j.v leave alone, nonoverlapping |
| * I.i.u I.i.v combine: Iivu |
| * |
| * R.i-j.u R.x-y.v | i-j in x-y delete first R |
| * R.i-j.u R.i-j.v delete first R |
| * R.i-j.u R.x-y.v | x-y in i-j ERROR |
| * R.i-j.u R.x-y.v | boundaries overlap ERROR |
| * |
| * Delete special case of replace (text==null): |
| * D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right) |
| * |
| * I.i.u R.x-y.v | i in (x+1)-y delete I (since insert before |
| * we're not deleting i) |
| * I.i.u R.x-y.v | i not in (x+1)-y leave alone, nonoverlapping |
| * R.x-y.v I.i.u | i in x-y ERROR |
| * R.x-y.v I.x.u R.x-y.uv (combine, delete I) |
| * R.x-y.v I.i.u | i not in x-y leave alone, nonoverlapping |
| * |
| * I.i.u = insert u before op @ index i |
| * R.x-y.u = replace x-y indexed tokens with u |
| * |
| * First we need to examine replaces. For any replace op: |
| * |
| * 1. wipe out any insertions before op within that range. |
| * 2. Drop any replace op before that is contained completely within |
| * that range. |
| * 3. Throw exception upon boundary overlap with any previous replace. |
| * |
| * Then we can deal with inserts: |
| * |
| * 1. for any inserts to same index, combine even if not adjacent. |
| * 2. for any prior replace with same left boundary, combine this |
| * insert with replace and delete this replace. |
| * 3. throw exception if index in same range as previous replace |
| * |
| * Don't actually delete; make op null in list. Easier to walk list. |
| * Later we can throw as we add to index -> op map. |
| * |
| * Note that I.2 R.2-2 will wipe out I.2 even though, technically, the |
| * inserted stuff would be before the replace range. But, if you |
| * add tokens in front of a method body '{' and then delete the method |
| * body, I think the stuff before the '{' you added should disappear too. |
| * |
| * Return a map from token index to operation. |
| */ |
| protected virtual IDictionary<int, RewriteOperation> ReduceToSingleOperationPerIndex( IList<RewriteOperation> rewrites ) |
| { |
| //System.out.println("rewrites="+rewrites); |
| |
| // WALK REPLACES |
| for ( int i = 0; i < rewrites.Count; i++ ) |
| { |
| RewriteOperation op = rewrites[i]; |
| if ( op == null ) |
| continue; |
| if ( !( op is ReplaceOp ) ) |
| continue; |
| ReplaceOp rop = (ReplaceOp)rewrites[i]; |
| // Wipe prior inserts within range |
| var inserts = GetKindOfOps( rewrites, typeof( InsertBeforeOp ), i ); |
| for ( int j = 0; j < inserts.Count; j++ ) |
| { |
| InsertBeforeOp iop = (InsertBeforeOp)inserts[j]; |
| if (iop.index == rop.index) |
| { |
| // E.g., insert before 2, delete 2..2; update replace |
| // text to include insert before, kill insert |
| rewrites[iop.instructionIndex] = null; |
| rop.text = iop.text.ToString() + (rop.text != null ? rop.text.ToString() : string.Empty); |
| } |
| else if (iop.index > rop.index && iop.index <= rop.lastIndex) |
| { |
| // delete insert as it's a no-op. |
| rewrites[iop.instructionIndex] = null; |
| } |
| } |
| // Drop any prior replaces contained within |
| var prevReplaces = GetKindOfOps( rewrites, typeof( ReplaceOp ), i ); |
| for ( int j = 0; j < prevReplaces.Count; j++ ) |
| { |
| ReplaceOp prevRop = (ReplaceOp)prevReplaces[j]; |
| if ( prevRop.index >= rop.index && prevRop.lastIndex <= rop.lastIndex ) |
| { |
| // delete replace as it's a no-op. |
| rewrites[prevRop.instructionIndex] = null; |
| continue; |
| } |
| // throw exception unless disjoint or identical |
| bool disjoint = |
| prevRop.lastIndex < rop.index || prevRop.index > rop.lastIndex; |
| bool same = |
| prevRop.index == rop.index && prevRop.lastIndex == rop.lastIndex; |
| // Delete special case of replace (text==null): |
| // D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right) |
| if (prevRop.text == null && rop.text == null && !disjoint) |
| { |
| //System.out.println("overlapping deletes: "+prevRop+", "+rop); |
| rewrites[prevRop.instructionIndex] = null; // kill first delete |
| rop.index = Math.Min(prevRop.index, rop.index); |
| rop.lastIndex = Math.Max(prevRop.lastIndex, rop.lastIndex); |
| Console.WriteLine("new rop " + rop); |
| } |
| else if ( !disjoint && !same ) |
| { |
| throw new ArgumentException( "replace op boundaries of " + rop + |
| " overlap with previous " + prevRop ); |
| } |
| } |
| } |
| |
| // WALK INSERTS |
| for ( int i = 0; i < rewrites.Count; i++ ) |
| { |
| RewriteOperation op = (RewriteOperation)rewrites[i]; |
| if ( op == null ) |
| continue; |
| if ( !( op is InsertBeforeOp ) ) |
| continue; |
| InsertBeforeOp iop = (InsertBeforeOp)rewrites[i]; |
| // combine current insert with prior if any at same index |
| var prevInserts = GetKindOfOps( rewrites, typeof( InsertBeforeOp ), i ); |
| for ( int j = 0; j < prevInserts.Count; j++ ) |
| { |
| InsertBeforeOp prevIop = (InsertBeforeOp)prevInserts[j]; |
| if ( prevIop.index == iop.index ) |
| { // combine objects |
| // convert to strings...we're in process of toString'ing |
| // whole token buffer so no lazy eval issue with any templates |
| iop.text = CatOpText( iop.text, prevIop.text ); |
| // delete redundant prior insert |
| rewrites[prevIop.instructionIndex] = null; |
| } |
| } |
| // look for replaces where iop.index is in range; error |
| var prevReplaces = GetKindOfOps( rewrites, typeof( ReplaceOp ), i ); |
| for ( int j = 0; j < prevReplaces.Count; j++ ) |
| { |
| ReplaceOp rop = (ReplaceOp)prevReplaces[j]; |
| if ( iop.index == rop.index ) |
| { |
| rop.text = CatOpText( iop.text, rop.text ); |
| rewrites[i] = null; // delete current insert |
| continue; |
| } |
| if ( iop.index >= rop.index && iop.index <= rop.lastIndex ) |
| { |
| throw new ArgumentException( "insert op " + iop + |
| " within boundaries of previous " + rop ); |
| } |
| } |
| } |
| // System.out.println("rewrites after="+rewrites); |
| IDictionary<int, RewriteOperation> m = new Dictionary<int, RewriteOperation>(); |
| for ( int i = 0; i < rewrites.Count; i++ ) |
| { |
| RewriteOperation op = (RewriteOperation)rewrites[i]; |
| if ( op == null ) |
| continue; // ignore deleted ops |
| |
| RewriteOperation existing; |
| if ( m.TryGetValue( op.index, out existing ) && existing != null ) |
| { |
| throw new Exception( "should only be one op per index" ); |
| } |
| m[op.index] = op; |
| } |
| //System.out.println("index to op: "+m); |
| return m; |
| } |
| |
| protected virtual string CatOpText( object a, object b ) |
| { |
| return string.Concat( a, b ); |
| } |
| protected virtual IList<RewriteOperation> GetKindOfOps( IList<RewriteOperation> rewrites, Type kind ) |
| { |
| return GetKindOfOps( rewrites, kind, rewrites.Count ); |
| } |
| |
| /** <summary>Get all operations before an index of a particular kind</summary> */ |
| protected virtual IList<RewriteOperation> GetKindOfOps( IList<RewriteOperation> rewrites, Type kind, int before ) |
| { |
| IList<RewriteOperation> ops = new List<RewriteOperation>(); |
| for ( int i = 0; i < before && i < rewrites.Count; i++ ) |
| { |
| RewriteOperation op = rewrites[i]; |
| if ( op == null ) |
| continue; // ignore deleted |
| if ( op.GetType() == kind ) |
| ops.Add( op ); |
| } |
| return ops; |
| } |
| |
| public virtual string ToDebugString() |
| { |
| return ToDebugString( MIN_TOKEN_INDEX, Count - 1 ); |
| } |
| |
| public virtual string ToDebugString( int start, int end ) |
| { |
| StringBuilder buf = new StringBuilder(); |
| for ( int i = start; i >= MIN_TOKEN_INDEX && i <= end && i < _tokens.Count; i++ ) |
| { |
| buf.Append( Get( i ) ); |
| } |
| return buf.ToString(); |
| } |
| } |
| } |