package eval
import (
)
type effectOp interface{ exec(*Frame) Exception }
func ( *compiler) ( *parse.Chunk) effectOp {
return chunkOp{.Range(), .pipelineOps(.Pipelines)}
}
type chunkOp struct {
diag.Ranging
subops []effectOp
}
func ( chunkOp) ( *Frame) Exception {
for , := range .subops {
:= .exec()
if != nil {
return
}
}
if .IsInterrupted() {
return .errorp(, ErrInterrupted)
}
return nil
}
func ( *compiler) ( *parse.Pipeline) effectOp {
:= .formOps(.Forms)
return &pipelineOp{.Range(), .Background, parse.SourceText(), }
}
func ( *compiler) ( []*parse.Pipeline) []effectOp {
:= make([]effectOp, len())
for , := range {
[] = .pipelineOp()
}
return
}
type pipelineOp struct {
diag.Ranging
bg bool
source string
subops []effectOp
}
const pipelineChanBufferSize = 32
func ( *pipelineOp) ( *Frame) Exception {
if .IsInterrupted() {
return .errorp(, ErrInterrupted)
}
if .bg {
= .fork("background job" + .source)
.intCh = nil
.background = true
.Evaler.addNumBgJobs(1)
}
:= len(.subops)
var sync.WaitGroup
.Add()
:= make([]Exception, )
var *Port
for , := range .subops {
:= > 0
:= .fork("[form op]")
if > 0 {
.ports[0] =
}
if < -1 {
, , := os.Pipe()
if != nil {
return .errorpf(, "failed to create pipe: %s", )
}
:= make(chan interface{}, pipelineChanBufferSize)
.ports[1] = &Port{
File: , Chan: , closeFile: true, closeChan: true}
= &Port{
File: , Chan: , closeFile: true, closeChan: false}
}
:=
:= &[]
go func() {
:= .exec()
.Close()
if != nil {
* =
}
.Done()
if {
for range .InputChan() {
}
}
}()
}
if .bg {
go func() {
.Wait()
.Evaler.addNumBgJobs(-1)
:= "job " + .source + " finished"
:= MakePipelineError()
if != nil {
+= ", errors = " + .Error()
}
if .Evaler.getNotifyBgJobSuccess() || != nil {
.ErrorFile().WriteString( + "\n")
}
}()
return nil
}
.Wait()
return .errorp(, MakePipelineError())
}
func ( *compiler) ( *parse.Form) effectOp {
var []lvalue
var []effectOp
if len(.Assignments) > 0 {
= .assignmentOps(.Assignments)
if .Head == nil {
.errorpf(, `using the syntax of temporary assignment for non-temporary assignment is no longer supported; use "var" or "set" instead`)
return nopOp{}
}
for , := range .Assignments {
:= .parseIndexingLValue(.Left)
= append(, .lvalues...)
}
logger.Println("temporary assignment of", len(.Assignments), "pairs")
}
:= .redirOps(.Redirs)
:= .formBody()
return &formOp{.Range(), , , , }
}
func ( *compiler) ( *parse.Form) formBody {
if .Head == nil {
return formBody{}
}
if , := cmpd.StringLiteral(.Head); {
, := resolveCmdHeadInternally(, , .Head)
if != nil {
:= (, )
return formBody{specialOp: }
}
}
for , := range .Args {
if parse.SourceText() == "=" {
:= make([]*parse.Compound, +1)
[0] = .Head
copy([1:], .Args[:])
:= .parseCompoundLValues()
:= .compoundOps(.Args[+1:])
var diag.Ranging
if len() > 0 {
= diag.MixedRanging([0], [len()-1])
} else {
= diag.PointRanging(.Range().To)
}
:= seqValuesOp{, }
return formBody{assignOp: &assignOp{.Range(), , }}
}
}
var valuesOp
if , := cmpd.StringLiteral(.Head); {
if , := resolveCmdHeadInternally(, , .Head); != nil {
= variableOp{.Head.Range(), false, + FnSuffix, }
} else {
= literalValues(.Head, NewExternalCmd())
}
} else {
= .compoundOp(.Head)
}
:= .compoundOps(.Args)
:= .mapPairs(.Opts)
return formBody{ordinaryCmd: ordinaryCmd{, , }}
}
func ( *compiler) ( []*parse.Form) []effectOp {
:= make([]effectOp, len())
for , := range {
[] = .formOp()
}
return
}
type formOp struct {
diag.Ranging
tempLValues []lvalue
tempAssignOps []effectOp
redirOps []effectOp
body formBody
}
type formBody struct {
specialOp effectOp
assignOp effectOp
ordinaryCmd ordinaryCmd
}
type ordinaryCmd struct {
headOp valuesOp
argOps []valuesOp
optsOp *mapPairsOp
}
func ( *formOp) ( *Frame) ( Exception) {
if len(.tempLValues) > 0 {
var []vars.Var
var []interface{}
for , := range .tempLValues {
, := derefLValue(, )
if != nil {
return .errorp(, )
}
= append(, )
}
for , := range {
if := vars.HeadOfElement(); != nil {
=
[] =
}
:= .Get()
= append(, )
logger.Printf("saved %s = %s", , )
}
for , := range .tempAssignOps {
:= .exec()
if != nil {
return
}
}
defer func() {
for , := range {
:= []
if == nil {
= ""
}
:= .Set()
if != nil {
= .errorp(, )
}
logger.Printf("restored %s = %s", , )
}
}()
}
for , := range .redirOps {
:= .exec()
if != nil {
return
}
}
if .body.specialOp != nil {
return .body.specialOp.exec()
}
if .body.assignOp != nil {
return .body.assignOp.exec()
}
:= .body.ordinaryCmd
if .headOp == nil {
return nil
}
, := evalForCommand(, .headOp, "command")
if != nil {
return .errorp(.headOp, )
}
var []interface{}
for , := range .argOps {
, := .exec()
if != nil {
return
}
= append(, ...)
}
:= make(map[string]interface{})
:= .optsOp.exec(, func(, interface{}) Exception {
if , := .(string); {
[] =
return nil
}
return .errorp(, errs.BadValue{
What: "option key", Valid: "string", Actual: vals.Kind()})
})
if != nil {
return
}
.traceback = .addTraceback()
= .Call(, , )
if , := .(Exception); {
return
}
return &exception{, .traceback}
}
func ( *Frame, valuesOp, string) (Callable, error) {
, := evalForValue(, , )
if != nil {
return nil,
}
switch value := .(type) {
case Callable:
return , nil
case string:
if fsutil.DontSearch() {
return NewExternalCmd(), nil
}
}
return nil, .errorp(, errs.BadValue{
What: ,
Valid: "callable or string containing slash",
Actual: vals.Kind()})
}
func ( []interface{}) bool {
for , := range {
if !vals.Bool() {
return false
}
}
return true
}
func ( *compiler) ( *parse.Assignment) effectOp {
:= .parseIndexingLValue(.Left)
:= .compoundOp(.Right)
return &assignOp{.Range(), , }
}
func ( *compiler) ( []*parse.Assignment) []effectOp {
:= make([]effectOp, len())
for , := range {
[] = .assignmentOp()
}
return
}
const defaultFileRedirPerm = 0644
func ( *compiler) ( *parse.Redir) effectOp {
var valuesOp
if .Left != nil {
= .compoundOp(.Left)
}
:= makeFlag(.Mode)
if == -1 {
.errorpf(, "bad redirection sign")
}
return &redirOp{.Range(), , .compoundOp(.Right), .RightIsFd, .Mode, }
}
func ( *compiler) ( []*parse.Redir) []effectOp {
:= make([]effectOp, len())
for , := range {
[] = .redirOp()
}
return
}
func ( parse.RedirMode) int {
switch {
case parse.Read:
return os.O_RDONLY
case parse.Write:
return os.O_WRONLY | os.O_CREATE | os.O_TRUNC
case parse.ReadWrite:
return os.O_RDWR | os.O_CREATE
case parse.Append:
return os.O_WRONLY | os.O_CREATE | os.O_APPEND
default:
return -1
}
}
type redirOp struct {
diag.Ranging
dstOp valuesOp
srcOp valuesOp
srcIsFd bool
mode parse.RedirMode
flag int
}
type invalidFD struct{ fd int }
func ( invalidFD) () string { return fmt.Sprintf("invalid fd: %d", .fd) }
func ( parse.RedirMode) chan interface{} {
if == parse.Read {
return ClosedChan
}
return BlackholeChan
}
func ( *redirOp) ( *Frame) Exception {
var int
if .dstOp == nil {
switch .mode {
case parse.Read:
= 0
case parse.Write, parse.ReadWrite, parse.Append:
= 1
default:
return .errorpf(, "bad RedirMode; parser bug")
}
} else {
var error
, = evalForFd(, .dstOp, false, "redirection destination")
if != nil {
return .errorp(, )
}
}
growPorts(&.ports, +1)
.ports[].close()
if .srcIsFd {
, := evalForFd(, .srcOp, true, "redirection source")
if != nil {
return .errorp(, )
}
switch {
case == -1:
.ports[] = &Port{}
case >= len(.ports) || .ports[] == nil:
return .errorp(, invalidFD{})
default:
.ports[] = .ports[].fork()
}
return nil
}
, := evalForValue(, .srcOp, "redirection source")
if != nil {
return .errorp(, )
}
switch src := .(type) {
case string:
, := os.OpenFile(, .flag, defaultFileRedirPerm)
if != nil {
return .errorpf(, "failed to open file %s: %s", vals.Repr(, vals.NoPretty), )
}
.ports[] = &Port{File: , closeFile: true, Chan: chanForFileRedir(.mode)}
case vals.File:
.ports[] = &Port{File: , closeFile: false, Chan: chanForFileRedir(.mode)}
case vals.Pipe:
var *os.File
switch .mode {
case parse.Read:
= .ReadEnd
case parse.Write:
= .WriteEnd
default:
return .errorpf(, "can only use < or > with pipes")
}
.ports[] = &Port{File: , closeFile: false, Chan: chanForFileRedir(.mode)}
default:
return .errorp(.srcOp, errs.BadValue{
What: "redirection source",
Valid: "string, file or pipe", Actual: vals.Kind()})
}
return nil
}
func ( *[]*Port, int) {
if len(*) >= {
return
}
:= *
* = make([]*Port, )
copy(*, )
}
func ( *Frame, valuesOp, bool, string) (int, error) {
, := evalForValue(, , )
if != nil {
return -1,
}
switch {
case "stdin":
return 0, nil
case "stdout":
return 1, nil
case "stderr":
return 2, nil
}
var int
if vals.ScanToGo(, &) == nil {
return , nil
} else if == "-" && {
return -1, nil
}
:= "fd name or number"
if {
= "fd name or number or '-'"
}
return -1, .errorp(, errs.BadValue{
What: , Valid: , Actual: vals.Repr(, vals.NoPretty)})
}
type seqOp struct{ subops []effectOp }
func ( seqOp) ( *Frame) Exception {
for , := range .subops {
:= .exec()
if != nil {
return
}
}
return nil
}
type nopOp struct{}
func (nopOp) ( *Frame) Exception { return nil }