public virtual void Replace(string programName, int from, int to, object text) { if (from > to || from < 0 || to < 0 || to >= tokens.Size) { throw new ArgumentException("replace: range invalid: " + from + ".." + to + "(size=" + tokens.Size + ")"); } TokenStreamRewriter.RewriteOperation op = new TokenStreamRewriter.ReplaceOp(tokens, from, to, text); IList <TokenStreamRewriter.RewriteOperation> rewrites = GetProgram(programName); op.instructionIndex = rewrites.Count; rewrites.Add(op); }
/// <summary> /// We need to combine operations and report invalid operations (like /// overlapping replaces that are not completed nested). /// </summary> /// <remarks> /// 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. /// </remarks> protected internal virtual IDictionary <int, TokenStreamRewriter.RewriteOperation> ReduceToSingleOperationPerIndex(IList <TokenStreamRewriter.RewriteOperation> rewrites) { // System.out.println("rewrites="+rewrites); // WALK REPLACES for (int i = 0; i < rewrites.Count; i++) { TokenStreamRewriter.RewriteOperation op = rewrites[i]; if (op == null) { continue; } if (!(op is TokenStreamRewriter.ReplaceOp)) { continue; } TokenStreamRewriter.ReplaceOp rop = (TokenStreamRewriter.ReplaceOp)rewrites[i]; // Wipe prior inserts within range IList <TokenStreamRewriter.InsertBeforeOp> inserts = GetKindOfOps <TokenStreamRewriter.InsertBeforeOp>(rewrites, i); foreach (TokenStreamRewriter.InsertBeforeOp iop in inserts) { 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 IList <TokenStreamRewriter.ReplaceOp> prevReplaces = GetKindOfOps <TokenStreamRewriter.ReplaceOp>(rewrites, i); foreach (TokenStreamRewriter.ReplaceOp prevRop in prevReplaces) { 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); #if !PORTABLE System.Console.Out.WriteLine("new rop " + rop); #endif } else { if (!disjoint && !same) { throw new ArgumentException("replace op boundaries of " + rop + " overlap with previous " + prevRop); } } } } // WALK INSERTS for (int i_1 = 0; i_1 < rewrites.Count; i_1++) { TokenStreamRewriter.RewriteOperation op = rewrites[i_1]; if (op == null) { continue; } if (!(op is TokenStreamRewriter.InsertBeforeOp)) { continue; } TokenStreamRewriter.InsertBeforeOp iop = (TokenStreamRewriter.InsertBeforeOp)rewrites[i_1]; // combine current insert with prior if any at same index IList <TokenStreamRewriter.InsertBeforeOp> prevInserts = GetKindOfOps <TokenStreamRewriter.InsertBeforeOp>(rewrites, i_1); foreach (TokenStreamRewriter.InsertBeforeOp prevIop in prevInserts) { 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 IList <TokenStreamRewriter.ReplaceOp> prevReplaces = GetKindOfOps <TokenStreamRewriter.ReplaceOp>(rewrites, i_1); foreach (TokenStreamRewriter.ReplaceOp rop in prevReplaces) { if (iop.index == rop.index) { rop.text = CatOpText(iop.text, rop.text); rewrites[i_1] = 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, TokenStreamRewriter.RewriteOperation> m = new Dictionary <int, TokenStreamRewriter.RewriteOperation>(); for (int i_2 = 0; i_2 < rewrites.Count; i_2++) { TokenStreamRewriter.RewriteOperation op = rewrites[i_2]; if (op == null) { continue; } // ignore deleted ops if (m.ContainsKey(op.index)) { throw new InvalidOperationException("should only be one op per index"); } m[op.index] = op; } //System.out.println("index to op: "+m); return(m); }