diff options
Diffstat (limited to 'runtime/CSharp3/Sources/Antlr3.Runtime/TokenRewriteStream.cs')
-rw-r--r-- | runtime/CSharp3/Sources/Antlr3.Runtime/TokenRewriteStream.cs | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/runtime/CSharp3/Sources/Antlr3.Runtime/TokenRewriteStream.cs b/runtime/CSharp3/Sources/Antlr3.Runtime/TokenRewriteStream.cs new file mode 100644 index 0000000..e9ab4a9 --- /dev/null +++ b/runtime/CSharp3/Sources/Antlr3.Runtime/TokenRewriteStream.cs @@ -0,0 +1,729 @@ +/* + * [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(); + } + } +} |