2023-08-17 05:15:28 +00:00
|
|
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
2023-05-29 21:03:29 +00:00
|
|
|
// Use of this file is governed by the BSD 3-clause license that
|
|
|
|
// can be found in the LICENSE.txt file in the project root.
|
2023-08-17 05:15:28 +00:00
|
|
|
|
2023-05-29 21:03:29 +00:00
|
|
|
package antlr
|
|
|
|
|
|
|
|
import (
|
2023-08-17 05:15:28 +00:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2023-05-29 21:03:29 +00:00
|
|
|
)
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
//
|
2023-05-29 21:03:29 +00:00
|
|
|
// Useful for rewriting out a buffered input token stream after doing some
|
|
|
|
// augmentation or other manipulations on it.
|
|
|
|
|
|
|
|
// <p>
|
|
|
|
// You can insert stuff, replace, and delete chunks. Note that the operations
|
|
|
|
// are done lazily--only if you convert the buffer to a {@link String} with
|
|
|
|
// {@link TokenStream#getText()}. This is very efficient because you are not
|
|
|
|
// moving data around all the time. As the buffer of tokens is converted to
|
|
|
|
// strings, the {@link #getText()} method(s) scan the input token stream and
|
|
|
|
// check to see if there is an operation at the current index. If so, the
|
|
|
|
// operation is done and then normal {@link String} rendering continues on the
|
|
|
|
// buffer. This is like having multiple Turing machine instruction streams
|
|
|
|
// (programs) operating on a single input tape. :)</p>
|
|
|
|
// <p>
|
|
|
|
|
|
|
|
// This rewriter makes no modifications to the token stream. It does not ask the
|
|
|
|
// stream to fill itself up nor does it advance the input cursor. The token
|
|
|
|
// stream {@link TokenStream#index()} will return the same value before and
|
|
|
|
// after any {@link #getText()} call.</p>
|
|
|
|
|
|
|
|
// <p>
|
|
|
|
// The rewriter only works on tokens that you have in the buffer and ignores the
|
|
|
|
// current input cursor. If you are buffering tokens on-demand, calling
|
|
|
|
// {@link #getText()} halfway through the input will only do rewrites for those
|
|
|
|
// tokens in the first half of the file.</p>
|
|
|
|
|
|
|
|
// <p>
|
|
|
|
// Since the operations are done lazily at {@link #getText}-time, operations do
|
|
|
|
// not screw up the token index values. That is, an insert operation at token
|
|
|
|
// index {@code i} does not change the index values for tokens
|
|
|
|
// {@code i}+1..n-1.</p>
|
|
|
|
|
|
|
|
// <p>
|
|
|
|
// 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,</p>
|
|
|
|
|
|
|
|
// <pre>
|
|
|
|
// CharStream input = new ANTLRFileStream("input");
|
|
|
|
// TLexer lex = new TLexer(input);
|
|
|
|
// CommonTokenStream tokens = new CommonTokenStream(lex);
|
|
|
|
// T parser = new T(tokens);
|
|
|
|
// TokenStreamRewriter rewriter = new TokenStreamRewriter(tokens);
|
|
|
|
// parser.startRule();
|
|
|
|
// </pre>
|
|
|
|
|
|
|
|
// <p>
|
|
|
|
// Then in the rules, you can execute (assuming rewriter is visible):</p>
|
|
|
|
|
|
|
|
// <pre>
|
|
|
|
// Token t,u;
|
|
|
|
// ...
|
|
|
|
// rewriter.insertAfter(t, "text to put after t");}
|
|
|
|
// rewriter.insertAfter(u, "text after u");}
|
|
|
|
// System.out.println(rewriter.getText());
|
|
|
|
// </pre>
|
|
|
|
|
|
|
|
// <p>
|
|
|
|
// 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:</p>
|
|
|
|
|
|
|
|
// <pre>
|
|
|
|
// rewriter.insertAfter("pass1", t, "text to put after t");}
|
|
|
|
// rewriter.insertAfter("pass2", u, "text after u");}
|
|
|
|
// System.out.println(rewriter.getText("pass1"));
|
|
|
|
// System.out.println(rewriter.getText("pass2"));
|
|
|
|
// </pre>
|
|
|
|
|
|
|
|
// <p>
|
|
|
|
// If you don't use named rewrite streams, a "default" stream is used as the
|
|
|
|
// first example shows.</p>
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
const (
|
2024-08-19 08:01:33 +00:00
|
|
|
DefaultProgramName = "default"
|
|
|
|
ProgramInitSize = 100
|
|
|
|
MinTokenIndex = 0
|
2023-05-29 21:03:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Define the rewrite operation hierarchy
|
|
|
|
|
|
|
|
type RewriteOperation interface {
|
2024-08-19 08:01:33 +00:00
|
|
|
|
2023-05-29 21:03:29 +00:00
|
|
|
// Execute the rewrite operation by possibly adding to the buffer.
|
|
|
|
// Return the index of the next token to operate on.
|
2023-08-17 05:15:28 +00:00
|
|
|
Execute(buffer *bytes.Buffer) int
|
|
|
|
String() string
|
|
|
|
GetInstructionIndex() int
|
|
|
|
GetIndex() int
|
|
|
|
GetText() string
|
|
|
|
GetOpName() string
|
|
|
|
GetTokens() TokenStream
|
2023-05-29 21:03:29 +00:00
|
|
|
SetInstructionIndex(val int)
|
|
|
|
SetIndex(int)
|
|
|
|
SetText(string)
|
|
|
|
SetOpName(string)
|
|
|
|
SetTokens(TokenStream)
|
|
|
|
}
|
|
|
|
|
|
|
|
type BaseRewriteOperation struct {
|
|
|
|
//Current index of rewrites list
|
2024-08-19 08:01:33 +00:00
|
|
|
instructionIndex int
|
2023-05-29 21:03:29 +00:00
|
|
|
//Token buffer index
|
2023-08-17 05:15:28 +00:00
|
|
|
index int
|
2023-05-29 21:03:29 +00:00
|
|
|
//Substitution text
|
2023-08-17 05:15:28 +00:00
|
|
|
text string
|
2023-05-29 21:03:29 +00:00
|
|
|
//Actual operation name
|
2024-08-19 08:01:33 +00:00
|
|
|
opName string
|
2023-05-29 21:03:29 +00:00
|
|
|
//Pointer to token steam
|
2023-08-17 05:15:28 +00:00
|
|
|
tokens TokenStream
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) GetInstructionIndex() int {
|
2024-08-19 08:01:33 +00:00
|
|
|
return op.instructionIndex
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) GetIndex() int {
|
2023-05-29 21:03:29 +00:00
|
|
|
return op.index
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) GetText() string {
|
2023-05-29 21:03:29 +00:00
|
|
|
return op.text
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) GetOpName() string {
|
2024-08-19 08:01:33 +00:00
|
|
|
return op.opName
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) GetTokens() TokenStream {
|
2023-05-29 21:03:29 +00:00
|
|
|
return op.tokens
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) SetInstructionIndex(val int) {
|
2024-08-19 08:01:33 +00:00
|
|
|
op.instructionIndex = val
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) SetIndex(val int) {
|
2023-05-29 21:03:29 +00:00
|
|
|
op.index = val
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) SetText(val string) {
|
2023-05-29 21:03:29 +00:00
|
|
|
op.text = val
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) SetOpName(val string) {
|
2024-08-19 08:01:33 +00:00
|
|
|
op.opName = val
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) SetTokens(val TokenStream) {
|
2023-05-29 21:03:29 +00:00
|
|
|
op.tokens = val
|
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (op *BaseRewriteOperation) Execute(_ *bytes.Buffer) int {
|
2023-05-29 21:03:29 +00:00
|
|
|
return op.index
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *BaseRewriteOperation) String() string {
|
2023-05-29 21:03:29 +00:00
|
|
|
return fmt.Sprintf("<%s@%d:\"%s\">",
|
2024-08-19 08:01:33 +00:00
|
|
|
op.opName,
|
2023-05-29 21:03:29 +00:00
|
|
|
op.tokens.Get(op.GetIndex()),
|
|
|
|
op.text,
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
type InsertBeforeOp struct {
|
|
|
|
BaseRewriteOperation
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func NewInsertBeforeOp(index int, text string, stream TokenStream) *InsertBeforeOp {
|
|
|
|
return &InsertBeforeOp{BaseRewriteOperation: BaseRewriteOperation{
|
2024-08-19 08:01:33 +00:00
|
|
|
index: index,
|
|
|
|
text: text,
|
|
|
|
opName: "InsertBeforeOp",
|
|
|
|
tokens: stream,
|
2023-05-29 21:03:29 +00:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *InsertBeforeOp) Execute(buffer *bytes.Buffer) int {
|
2023-05-29 21:03:29 +00:00
|
|
|
buffer.WriteString(op.text)
|
2023-08-17 05:15:28 +00:00
|
|
|
if op.tokens.Get(op.index).GetTokenType() != TokenEOF {
|
2023-05-29 21:03:29 +00:00
|
|
|
buffer.WriteString(op.tokens.Get(op.index).GetText())
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
return op.index + 1
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (op *InsertBeforeOp) String() string {
|
|
|
|
return op.BaseRewriteOperation.String()
|
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
// InsertAfterOp distinguishes between insert after/before to do the "insert after" instructions
|
|
|
|
// first and then the "insert before" instructions at same index. Implementation
|
|
|
|
// of "insert after" is "insert before index+1".
|
2023-05-29 21:03:29 +00:00
|
|
|
type InsertAfterOp struct {
|
|
|
|
BaseRewriteOperation
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func NewInsertAfterOp(index int, text string, stream TokenStream) *InsertAfterOp {
|
2024-08-19 08:01:33 +00:00
|
|
|
return &InsertAfterOp{
|
|
|
|
BaseRewriteOperation: BaseRewriteOperation{
|
|
|
|
index: index + 1,
|
|
|
|
text: text,
|
|
|
|
tokens: stream,
|
|
|
|
},
|
|
|
|
}
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (op *InsertAfterOp) Execute(buffer *bytes.Buffer) int {
|
|
|
|
buffer.WriteString(op.text)
|
2023-08-17 05:15:28 +00:00
|
|
|
if op.tokens.Get(op.index).GetTokenType() != TokenEOF {
|
2023-05-29 21:03:29 +00:00
|
|
|
buffer.WriteString(op.tokens.Get(op.index).GetText())
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
return op.index + 1
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (op *InsertAfterOp) String() string {
|
|
|
|
return op.BaseRewriteOperation.String()
|
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
// ReplaceOp tries to replace range from x..y with (y-x)+1 ReplaceOp
|
2023-05-29 21:03:29 +00:00
|
|
|
// instructions.
|
2023-08-17 05:15:28 +00:00
|
|
|
type ReplaceOp struct {
|
2023-05-29 21:03:29 +00:00
|
|
|
BaseRewriteOperation
|
|
|
|
LastIndex int
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func NewReplaceOp(from, to int, text string, stream TokenStream) *ReplaceOp {
|
2023-05-29 21:03:29 +00:00
|
|
|
return &ReplaceOp{
|
2023-08-17 05:15:28 +00:00
|
|
|
BaseRewriteOperation: BaseRewriteOperation{
|
2024-08-19 08:01:33 +00:00
|
|
|
index: from,
|
|
|
|
text: text,
|
|
|
|
opName: "ReplaceOp",
|
|
|
|
tokens: stream,
|
2023-05-29 21:03:29 +00:00
|
|
|
},
|
2023-08-17 05:15:28 +00:00
|
|
|
LastIndex: to,
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (op *ReplaceOp) Execute(buffer *bytes.Buffer) int {
|
|
|
|
if op.text != "" {
|
2023-05-29 21:03:29 +00:00
|
|
|
buffer.WriteString(op.text)
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
return op.LastIndex + 1
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (op *ReplaceOp) String() string {
|
|
|
|
if op.text == "" {
|
|
|
|
return fmt.Sprintf("<DeleteOP@%d..%d>",
|
|
|
|
op.tokens.Get(op.index), op.tokens.Get(op.LastIndex))
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("<ReplaceOp@%d..%d:\"%s\">",
|
|
|
|
op.tokens.Get(op.index), op.tokens.Get(op.LastIndex), op.text)
|
|
|
|
}
|
|
|
|
|
|
|
|
type TokenStreamRewriter struct {
|
|
|
|
//Our source stream
|
2023-08-17 05:15:28 +00:00
|
|
|
tokens TokenStream
|
2023-05-29 21:03:29 +00:00
|
|
|
// You may have multiple, named streams of rewrite operations.
|
|
|
|
// I'm calling these things "programs."
|
|
|
|
// Maps String (name) → rewrite (List)
|
2024-08-19 08:01:33 +00:00
|
|
|
programs map[string][]RewriteOperation
|
|
|
|
lastRewriteTokenIndexes map[string]int
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func NewTokenStreamRewriter(tokens TokenStream) *TokenStreamRewriter {
|
2023-05-29 21:03:29 +00:00
|
|
|
return &TokenStreamRewriter{
|
2023-08-17 05:15:28 +00:00
|
|
|
tokens: tokens,
|
|
|
|
programs: map[string][]RewriteOperation{
|
2024-08-19 08:01:33 +00:00
|
|
|
DefaultProgramName: make([]RewriteOperation, 0, ProgramInitSize),
|
2023-05-29 21:03:29 +00:00
|
|
|
},
|
2024-08-19 08:01:33 +00:00
|
|
|
lastRewriteTokenIndexes: map[string]int{},
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) GetTokenStream() TokenStream {
|
2023-05-29 21:03:29 +00:00
|
|
|
return tsr.tokens
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
// Rollback the instruction stream for a program so that
|
|
|
|
// the indicated instruction (via instructionIndex) is no
|
|
|
|
// longer in the stream. UNTESTED!
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) Rollback(programName string, instructionIndex int) {
|
|
|
|
is, ok := tsr.programs[programName]
|
2023-08-17 05:15:28 +00:00
|
|
|
if ok {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.programs[programName] = is[MinTokenIndex:instructionIndex]
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) RollbackDefault(instructionIndex int) {
|
|
|
|
tsr.Rollback(DefaultProgramName, instructionIndex)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
// DeleteProgram Reset the program so that no instructions exist
|
|
|
|
func (tsr *TokenStreamRewriter) DeleteProgram(programName string) {
|
|
|
|
tsr.Rollback(programName, MinTokenIndex) //TODO: double test on that cause lower bound is not included
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) DeleteProgramDefault() {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.DeleteProgram(DefaultProgramName)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) InsertAfter(programName string, index int, text string) {
|
2023-05-29 21:03:29 +00:00
|
|
|
// to insert after, just insert before next index (even if past end)
|
|
|
|
var op RewriteOperation = NewInsertAfterOp(index, text, tsr.tokens)
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites := tsr.GetProgram(programName)
|
2023-05-29 21:03:29 +00:00
|
|
|
op.SetInstructionIndex(len(rewrites))
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.AddToProgram(programName, op)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) InsertAfterDefault(index int, text string) {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.InsertAfter(DefaultProgramName, index, text)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) InsertAfterToken(programName string, token Token, text string) {
|
|
|
|
tsr.InsertAfter(programName, token.GetTokenIndex(), text)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) InsertBefore(programName string, index int, text string) {
|
2023-05-29 21:03:29 +00:00
|
|
|
var op RewriteOperation = NewInsertBeforeOp(index, text, tsr.tokens)
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites := tsr.GetProgram(programName)
|
2023-05-29 21:03:29 +00:00
|
|
|
op.SetInstructionIndex(len(rewrites))
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.AddToProgram(programName, op)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) InsertBeforeDefault(index int, text string) {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.InsertBefore(DefaultProgramName, index, text)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) InsertBeforeToken(programName string, token Token, text string) {
|
|
|
|
tsr.InsertBefore(programName, token.GetTokenIndex(), text)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) Replace(programName string, from, to int, text string) {
|
2023-08-17 05:15:28 +00:00
|
|
|
if from > to || from < 0 || to < 0 || to >= tsr.tokens.Size() {
|
2023-05-29 21:03:29 +00:00
|
|
|
panic(fmt.Sprintf("replace: range invalid: %d..%d(size=%d)",
|
|
|
|
from, to, tsr.tokens.Size()))
|
|
|
|
}
|
|
|
|
var op RewriteOperation = NewReplaceOp(from, to, text, tsr.tokens)
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites := tsr.GetProgram(programName)
|
2023-05-29 21:03:29 +00:00
|
|
|
op.SetInstructionIndex(len(rewrites))
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.AddToProgram(programName, op)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) ReplaceDefault(from, to int, text string) {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.Replace(DefaultProgramName, from, to, text)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) ReplaceDefaultPos(index int, text string) {
|
2023-05-29 21:03:29 +00:00
|
|
|
tsr.ReplaceDefault(index, index, text)
|
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) ReplaceToken(programName string, from, to Token, text string) {
|
|
|
|
tsr.Replace(programName, from.GetTokenIndex(), to.GetTokenIndex(), text)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) ReplaceTokenDefault(from, to Token, text string) {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.ReplaceToken(DefaultProgramName, from, to, text)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) ReplaceTokenDefaultPos(index Token, text string) {
|
2023-05-29 21:03:29 +00:00
|
|
|
tsr.ReplaceTokenDefault(index, index, text)
|
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) Delete(programName string, from, to int) {
|
|
|
|
tsr.Replace(programName, from, to, "")
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) DeleteDefault(from, to int) {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.Delete(DefaultProgramName, from, to)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) DeleteDefaultPos(index int) {
|
|
|
|
tsr.DeleteDefault(index, index)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) DeleteToken(programName string, from, to Token) {
|
|
|
|
tsr.ReplaceToken(programName, from, to, "")
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) DeleteTokenDefault(from, to Token) {
|
2024-08-19 08:01:33 +00:00
|
|
|
tsr.DeleteToken(DefaultProgramName, from, to)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) GetLastRewriteTokenIndex(programName string) int {
|
|
|
|
i, ok := tsr.lastRewriteTokenIndexes[programName]
|
2023-08-17 05:15:28 +00:00
|
|
|
if !ok {
|
2023-05-29 21:03:29 +00:00
|
|
|
return -1
|
|
|
|
}
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) GetLastRewriteTokenIndexDefault() int {
|
2024-08-19 08:01:33 +00:00
|
|
|
return tsr.GetLastRewriteTokenIndex(DefaultProgramName)
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) SetLastRewriteTokenIndex(programName string, i int) {
|
|
|
|
tsr.lastRewriteTokenIndexes[programName] = i
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) InitializeProgram(name string) []RewriteOperation {
|
2024-08-19 08:01:33 +00:00
|
|
|
is := make([]RewriteOperation, 0, ProgramInitSize)
|
2023-05-29 21:03:29 +00:00
|
|
|
tsr.programs[name] = is
|
|
|
|
return is
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) AddToProgram(name string, op RewriteOperation) {
|
2023-05-29 21:03:29 +00:00
|
|
|
is := tsr.GetProgram(name)
|
|
|
|
is = append(is, op)
|
|
|
|
tsr.programs[name] = is
|
|
|
|
}
|
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func (tsr *TokenStreamRewriter) GetProgram(name string) []RewriteOperation {
|
2023-05-29 21:03:29 +00:00
|
|
|
is, ok := tsr.programs[name]
|
2023-08-17 05:15:28 +00:00
|
|
|
if !ok {
|
2023-05-29 21:03:29 +00:00
|
|
|
is = tsr.InitializeProgram(name)
|
|
|
|
}
|
|
|
|
return is
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
// GetTextDefault returns the text from the original tokens altered per the
|
2023-08-17 05:15:28 +00:00
|
|
|
// instructions given to this rewriter.
|
|
|
|
func (tsr *TokenStreamRewriter) GetTextDefault() string {
|
2023-05-29 21:03:29 +00:00
|
|
|
return tsr.GetText(
|
2024-08-19 08:01:33 +00:00
|
|
|
DefaultProgramName,
|
2023-05-29 21:03:29 +00:00
|
|
|
NewInterval(0, tsr.tokens.Size()-1))
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
// GetText returns the text from the original tokens altered per the
|
2023-08-17 05:15:28 +00:00
|
|
|
// instructions given to this rewriter.
|
2024-08-19 08:01:33 +00:00
|
|
|
func (tsr *TokenStreamRewriter) GetText(programName string, interval Interval) string {
|
|
|
|
rewrites := tsr.programs[programName]
|
2023-05-29 21:03:29 +00:00
|
|
|
start := interval.Start
|
2023-08-17 05:15:28 +00:00
|
|
|
stop := interval.Stop
|
2023-05-29 21:03:29 +00:00
|
|
|
// ensure start/end are in range
|
|
|
|
stop = min(stop, tsr.tokens.Size()-1)
|
2023-08-17 05:15:28 +00:00
|
|
|
start = max(start, 0)
|
2024-08-19 08:01:33 +00:00
|
|
|
if len(rewrites) == 0 {
|
2023-05-29 21:03:29 +00:00
|
|
|
return tsr.tokens.GetTextFromInterval(interval) // no instructions to execute
|
|
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
|
|
// First, optimize instruction stream
|
|
|
|
indexToOp := reduceToSingleOperationPerIndex(rewrites)
|
|
|
|
// Walk buffer, executing instructions and emitting tokens
|
2023-08-17 05:15:28 +00:00
|
|
|
for i := start; i <= stop && i < tsr.tokens.Size(); {
|
2023-05-29 21:03:29 +00:00
|
|
|
op := indexToOp[i]
|
2023-08-17 05:15:28 +00:00
|
|
|
delete(indexToOp, i) // remove so any left have index size-1
|
2023-05-29 21:03:29 +00:00
|
|
|
t := tsr.tokens.Get(i)
|
2023-08-17 05:15:28 +00:00
|
|
|
if op == nil {
|
2023-05-29 21:03:29 +00:00
|
|
|
// no operation at that index, just dump token
|
2023-08-17 05:15:28 +00:00
|
|
|
if t.GetTokenType() != TokenEOF {
|
|
|
|
buf.WriteString(t.GetText())
|
|
|
|
}
|
2023-05-29 21:03:29 +00:00
|
|
|
i++ // move to next token
|
2023-08-17 05:15:28 +00:00
|
|
|
} else {
|
|
|
|
i = op.Execute(&buf) // execute operation and skip
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// include stuff after end if it's last index in buffer
|
|
|
|
// So, if they did an insertAfter(lastValidIndex, "foo"), include
|
|
|
|
// foo if end==lastValidIndex.
|
2023-08-17 05:15:28 +00:00
|
|
|
if stop == tsr.tokens.Size()-1 {
|
2023-05-29 21:03:29 +00:00
|
|
|
// Scan any remaining operations after last token
|
|
|
|
// should be included (they will be inserts).
|
2023-08-17 05:15:28 +00:00
|
|
|
for _, op := range indexToOp {
|
|
|
|
if op.GetIndex() >= tsr.tokens.Size()-1 {
|
|
|
|
buf.WriteString(op.GetText())
|
|
|
|
}
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2024-08-19 08:01:33 +00:00
|
|
|
// reduceToSingleOperationPerIndex combines 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:
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// I.i.u I.j.v leave alone, non-overlapping
|
2023-08-17 05:15:28 +00:00
|
|
|
// I.i.u I.i.v combine: Iivu
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2023-08-17 05:15:28 +00:00
|
|
|
// 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
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2023-08-17 05:15:28 +00:00
|
|
|
// Delete special case of replace (text==null):
|
|
|
|
// D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right)
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2023-08-17 05:15:28 +00:00
|
|
|
// I.i.u R.x-y.v | i in (x+1)-y delete I (since insert before
|
2024-08-19 08:01:33 +00:00
|
|
|
// we're not deleting i)
|
|
|
|
// I.i.u R.x-y.v | i not in (x+1)-y leave alone, non-overlapping
|
2023-08-17 05:15:28 +00:00
|
|
|
// 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)
|
2024-08-19 08:01:33 +00:00
|
|
|
// R.x-y.v I.i.u | i not in x-y leave alone, non-overlapping
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2023-08-17 05:15:28 +00:00
|
|
|
// I.i.u = insert u before op @ index i
|
|
|
|
// R.x-y.u = replace x-y indexed tokens with u
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// First we need to examine replaces. For any replace op:
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// 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.
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// Then we can deal with inserts:
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// 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
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// Don't actually delete; make op null in list. Easier to walk list.
|
|
|
|
// Later we can throw as we add to index → op map.
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// 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.
|
2023-05-29 21:03:29 +00:00
|
|
|
//
|
2024-08-19 08:01:33 +00:00
|
|
|
// The func returns a map from token index to operation.
|
2023-08-17 05:15:28 +00:00
|
|
|
func reduceToSingleOperationPerIndex(rewrites []RewriteOperation) map[int]RewriteOperation {
|
2023-05-29 21:03:29 +00:00
|
|
|
// WALK REPLACES
|
2023-08-17 05:15:28 +00:00
|
|
|
for i := 0; i < len(rewrites); i++ {
|
2023-05-29 21:03:29 +00:00
|
|
|
op := rewrites[i]
|
2023-08-17 05:15:28 +00:00
|
|
|
if op == nil {
|
|
|
|
continue
|
|
|
|
}
|
2023-05-29 21:03:29 +00:00
|
|
|
rop, ok := op.(*ReplaceOp)
|
2023-08-17 05:15:28 +00:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2023-05-29 21:03:29 +00:00
|
|
|
// Wipe prior inserts within range
|
2023-08-17 05:15:28 +00:00
|
|
|
for j := 0; j < i && j < len(rewrites); j++ {
|
|
|
|
if iop, ok := rewrites[j].(*InsertBeforeOp); ok {
|
|
|
|
if iop.index == rop.index {
|
2023-05-29 21:03:29 +00:00
|
|
|
// E.g., insert before 2, delete 2..2; update replace
|
|
|
|
// text to include insert before, kill insert
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites[iop.instructionIndex] = nil
|
2023-08-17 05:15:28 +00:00
|
|
|
if rop.text != "" {
|
2023-05-29 21:03:29 +00:00
|
|
|
rop.text = iop.text + rop.text
|
2023-08-17 05:15:28 +00:00
|
|
|
} else {
|
2023-05-29 21:03:29 +00:00
|
|
|
rop.text = iop.text
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
} else if iop.index > rop.index && iop.index <= rop.LastIndex {
|
2023-05-29 21:03:29 +00:00
|
|
|
// delete insert as it's a no-op.
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites[iop.instructionIndex] = nil
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Drop any prior replaces contained within
|
2023-08-17 05:15:28 +00:00
|
|
|
for j := 0; j < i && j < len(rewrites); j++ {
|
|
|
|
if prevop, ok := rewrites[j].(*ReplaceOp); ok {
|
|
|
|
if prevop.index >= rop.index && prevop.LastIndex <= rop.LastIndex {
|
2023-05-29 21:03:29 +00:00
|
|
|
// delete replace as it's a no-op.
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites[prevop.instructionIndex] = nil
|
2023-05-29 21:03:29 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
// throw exception unless disjoint or identical
|
|
|
|
disjoint := prevop.LastIndex < rop.index || prevop.index > 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)
|
2023-08-17 05:15:28 +00:00
|
|
|
if prevop.text == "" && rop.text == "" && !disjoint {
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites[prevop.instructionIndex] = nil
|
2023-05-29 21:03:29 +00:00
|
|
|
rop.index = min(prevop.index, rop.index)
|
|
|
|
rop.LastIndex = max(prevop.LastIndex, rop.LastIndex)
|
2023-08-17 05:15:28 +00:00
|
|
|
} else if !disjoint {
|
2023-05-29 21:03:29 +00:00
|
|
|
panic("replace op boundaries of " + rop.String() + " overlap with previous " + prevop.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// WALK INSERTS
|
2023-08-17 05:15:28 +00:00
|
|
|
for i := 0; i < len(rewrites); i++ {
|
2023-05-29 21:03:29 +00:00
|
|
|
op := rewrites[i]
|
2023-08-17 05:15:28 +00:00
|
|
|
if op == nil {
|
|
|
|
continue
|
|
|
|
}
|
2023-05-29 21:03:29 +00:00
|
|
|
//hack to replicate inheritance in composition
|
|
|
|
_, iok := rewrites[i].(*InsertBeforeOp)
|
|
|
|
_, aok := rewrites[i].(*InsertAfterOp)
|
2023-08-17 05:15:28 +00:00
|
|
|
if !iok && !aok {
|
|
|
|
continue
|
|
|
|
}
|
2023-05-29 21:03:29 +00:00
|
|
|
iop := rewrites[i]
|
|
|
|
// combine current insert with prior if any at same index
|
|
|
|
// deviating a bit from TokenStreamRewriter.java - hard to incorporate inheritance logic
|
2023-08-17 05:15:28 +00:00
|
|
|
for j := 0; j < i && j < len(rewrites); j++ {
|
|
|
|
if nextIop, ok := rewrites[j].(*InsertAfterOp); ok {
|
|
|
|
if nextIop.index == iop.GetIndex() {
|
2023-05-29 21:03:29 +00:00
|
|
|
iop.SetText(nextIop.text + iop.GetText())
|
|
|
|
rewrites[j] = nil
|
|
|
|
}
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
if prevIop, ok := rewrites[j].(*InsertBeforeOp); ok {
|
|
|
|
if prevIop.index == iop.GetIndex() {
|
2023-05-29 21:03:29 +00:00
|
|
|
iop.SetText(iop.GetText() + prevIop.text)
|
2024-08-19 08:01:33 +00:00
|
|
|
rewrites[prevIop.instructionIndex] = nil
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// look for replaces where iop.index is in range; error
|
2023-08-17 05:15:28 +00:00
|
|
|
for j := 0; j < i && j < len(rewrites); j++ {
|
|
|
|
if rop, ok := rewrites[j].(*ReplaceOp); ok {
|
|
|
|
if iop.GetIndex() == rop.index {
|
2023-05-29 21:03:29 +00:00
|
|
|
rop.text = iop.GetText() + rop.text
|
|
|
|
rewrites[i] = nil
|
|
|
|
continue
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
if iop.GetIndex() >= rop.index && iop.GetIndex() <= rop.LastIndex {
|
|
|
|
panic("insert op " + iop.String() + " within boundaries of previous " + rop.String())
|
2023-05-29 21:03:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m := map[int]RewriteOperation{}
|
2023-08-17 05:15:28 +00:00
|
|
|
for i := 0; i < len(rewrites); i++ {
|
2023-05-29 21:03:29 +00:00
|
|
|
op := rewrites[i]
|
2023-08-17 05:15:28 +00:00
|
|
|
if op == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if _, ok := m[op.GetIndex()]; ok {
|
2023-05-29 21:03:29 +00:00
|
|
|
panic("should only be one op per index")
|
|
|
|
}
|
|
|
|
m[op.GetIndex()] = op
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Quick fixing Go lack of overloads
|
2023-08-17 05:15:28 +00:00
|
|
|
*/
|
2023-05-29 21:03:29 +00:00
|
|
|
|
2023-08-17 05:15:28 +00:00
|
|
|
func max(a, b int) int {
|
|
|
|
if a > b {
|
2023-05-29 21:03:29 +00:00
|
|
|
return a
|
2023-08-17 05:15:28 +00:00
|
|
|
} else {
|
2023-05-29 21:03:29 +00:00
|
|
|
return b
|
|
|
|
}
|
|
|
|
}
|
2023-08-17 05:15:28 +00:00
|
|
|
func min(a, b int) int {
|
|
|
|
if a < b {
|
2023-05-29 21:03:29 +00:00
|
|
|
return a
|
2023-08-17 05:15:28 +00:00
|
|
|
} else {
|
2023-05-29 21:03:29 +00:00
|
|
|
return b
|
|
|
|
}
|
|
|
|
}
|