packer-cn/vendor/github.com/rwtodd/Go.Sed/sed/engine.go

138 lines
4.4 KiB
Go

// Package sed implements the classic UNIX sed language in pure Go.
// The interface is very simple: a user compiles a program into an
// execution engine by calling New or NewQuiet. Then, the engine
// can Wrap() any io.Reader to lazily process the stream as you
// read from it.
//
// All classic sed commands are supported, but since the package
// uses Go's regexp package for the regular expressions, the syntax
// for regexps will not be the same as a typical UNIX sed. In other
// words, instead of: s|ab\(c*\)d|\1|g you would say: s|ab(c*)d|$1|g.
// So this is a Go-flavored sed, rather than a drop-in replacement for
// a UNIX sed. Depending on your tastes, you will either consider this
// an improvement or completely brain-dead.
package sed
import (
"bufio"
"bytes"
"strings"
"io"
)
// Engine is the compiled instruction stream for a sed program.
// It is the main type that users of the go-sed library will
// interact with.
type Engine struct {
ins []instruction // the instruction stream
}
// vm is the virtual machine state for a running sed program.
type vm struct {
nxtl string // the next line
pat string // the pattern space, possibly nil
hold string // the hold buffer, possibly nil
appl *string // any lines we've been asked to 'a\'ppend, usually nil
overflow string // any overflow we might have accumulated
lastl bool // true if it's the last line
ins []instruction // the instruction stream
ip int // the current locaiton in the instruction stream
input *bufio.Reader // the input stream
output []byte // the output buffer
lineno int // current line number
modified bool // have we modified the pattern space?
}
// a sed instruction is mostly a function transforming an engine
type instruction func(*vm) error
// makeEngine is the logic behine the New and NewQuiet public functions.
// It lexes and parses the program, and makes a new Engine out of it.
func makeEngine(program io.Reader, isQuiet bool) (*Engine, error) {
bufprog := bufio.NewReader(program)
ch := make(chan *token, 128)
errch := make(chan error, 1)
go lex(bufprog, ch, errch)
instructions, parseErr := parse(ch, isQuiet)
var err = <-errch // look for lexing errors first...
if err == nil {
// if there were no lex errors, look for a parsing error
err = parseErr
}
return &Engine{ins: instructions}, err
}
// New creates a new sed engine from a program. The program is executed
// via the Run method. If the provided program has any errors, the returned
// engine will be 'nil' and the error will be returned. Otherwise, the returned
// error will be nil.
func New(program io.Reader) (*Engine, error) {
return makeEngine(program, false)
}
// NewQuiet creates a new sed engine from a program. It behaves exactly as
// New(), except it produces an engine that doesn't print lines by defualt. This
// is the classic '-n' sed behaviour.
func NewQuiet(program io.Reader) (*Engine, error) {
return makeEngine(program, true)
}
// Wrap supplies an io.Reader that applies the sed Engine to the given
// input. The sed program is run lazily against the input as the user
// asks for bytes. If you'd prefer to run all at once from string to
// string, use RunString instead.
func (e *Engine) Wrap(input io.Reader) io.Reader {
bufin := bufio.NewReader(input)
// prime the engine by resetting the internal flags and filling nxtl...
return &vm{ins: e.ins, input: bufin, lineno: -1, ip: -1}
}
// Read turns a vm into an io.Reader.
func (v *vm) Read(p []byte) (int, error) {
var err error
v.output = p
if v.lineno == -1 {
// we have an uninitialized stream
err = cmd_fillNext(v)
v.ip = 0
} else if len(v.overflow) > 0 {
// we have overflow to work on
o := v.overflow
v.overflow = ""
err = writeString(v, o)
}
// run the program
for err == nil {
err = v.ins[v.ip](v)
}
var n int = len(p) - len(v.output)
if ((err == fullBuffer) || (err == io.EOF)) && (n > 0) {
err = nil
}
return n, err
}
// RunString executes the program embodied by the Engine on the
// given string as input, returning the output string and any
// errors that occured.
func (e *Engine) RunString(input string) (string, error) {
inbuf := strings.NewReader(input)
var outbytes bytes.Buffer
_, err := io.Copy(&outbytes, e.Wrap(inbuf))
if err == io.EOF {
err = nil
}
return outbytes.String(), err
}