Export eval.Closure and (*eval.Frame).Fork.

This commit is contained in:
Qi Xiao 2021-12-30 15:01:43 +00:00
parent 4906c4aa28
commit d9e14da0db
8 changed files with 46 additions and 45 deletions

View File

@ -84,7 +84,7 @@ func runParallel(fm *Frame, functions ...Callable) error {
*pexc = err.(Exception)
}
wg.Done()
}(fm.fork("[run-parallel function]"), function, &exceptions[i])
}(fm.Fork("[run-parallel function]"), function, &exceptions[i])
}
wg.Wait()
@ -130,7 +130,7 @@ func each(fm *Frame, f Callable, inputs Inputs) error {
if broken {
return
}
newFm := fm.fork("closure of each")
newFm := fm.Fork("closure of each")
ex := f.Call(newFm, []interface{}{v}, NoOpts)
newFm.Close()
@ -201,7 +201,7 @@ func peach(fm *Frame, f Callable, inputs Inputs) error {
}
wg.Add(1)
go func() {
newFm := fm.fork("closure of peach")
newFm := fm.Fork("closure of peach")
newFm.ports[0] = DummyInputPort
ex := f.Call(newFm, []interface{}{v}, NoOpts)
newFm.Close()

View File

@ -185,7 +185,7 @@ func call(fm *Frame, fn Callable, argsVal vals.List, optsVal vals.Map) error {
}
opts[ks] = v
}
return fn.Call(fm.fork("-call"), args, opts)
return fn.Call(fm.Fork("-call"), args, opts)
}
//elvdoc:fn resolve
@ -294,7 +294,7 @@ func eval(fm *Frame, opts evalOpts, code string) error {
// nil as the second argument.
newNs, exc := fm.Eval(src, nil, ns)
if opts.OnEnd != nil {
newFm := fm.fork("on-end callback of eval")
newFm := fm.Fork("on-end callback of eval")
errCb := opts.OnEnd.Call(newFm, []interface{}{newNs}, NoOpts)
if exc == nil {
return errCb
@ -518,7 +518,7 @@ func timeCmd(fm *Frame, opts timeOpt, f Callable) error {
dt := t1.Sub(t0)
if opts.OnEnd != nil {
newFm := fm.fork("on-end callback of time")
newFm := fm.Fork("on-end callback of time")
errCb := opts.OnEnd.Call(newFm, []interface{}{dt.Seconds()}, NoOpts)
if err == nil {
err = errCb

View File

@ -209,7 +209,7 @@ func eawk(fm *Frame, f Callable, inputs Inputs) error {
args = append(args, field)
}
newFm := fm.fork("fn of eawk")
newFm := fm.Fork("fn of eawk")
// TODO: Close port 0 of newFm.
ex := f.Call(newFm, args, NoOpts)
newFm.Close()

View File

@ -252,8 +252,8 @@ func (op fnOp) exec(fm *Frame) Exception {
if exc != nil {
return exc
}
c := values[0].(*closure)
c.Op = fnWrap{c.Op}
c := values[0].(*Closure)
c.op = fnWrap{c.op}
return fm.errorp(op.keywordRange, fm.local.slots[op.varIndex].Set(c))
}
@ -520,16 +520,16 @@ func (op *ifOp) exec(fm *Frame) Exception {
}
elseFn := execLambdaOp(fm, op.elseOp)
for i, condOp := range op.condOps {
condValues, exc := condOp.exec(fm.fork("if cond"))
condValues, exc := condOp.exec(fm.Fork("if cond"))
if exc != nil {
return exc
}
if allTrue(condValues) {
return fm.errorp(op, bodies[i].Call(fm.fork("if body"), NoArgs, NoOpts))
return fm.errorp(op, bodies[i].Call(fm.Fork("if body"), NoArgs, NoOpts))
}
}
if op.elseOp != nil {
return fm.errorp(op, elseFn.Call(fm.fork("if else"), NoArgs, NoOpts))
return fm.errorp(op, elseFn.Call(fm.Fork("if else"), NoArgs, NoOpts))
}
return nil
}
@ -562,7 +562,7 @@ func (op *whileOp) exec(fm *Frame) Exception {
iterated := false
for {
condValues, exc := op.condOp.exec(fm.fork("while cond"))
condValues, exc := op.condOp.exec(fm.Fork("while cond"))
if exc != nil {
return exc
}
@ -570,7 +570,7 @@ func (op *whileOp) exec(fm *Frame) Exception {
break
}
iterated = true
err := body.Call(fm.fork("while"), NoArgs, NoOpts)
err := body.Call(fm.Fork("while"), NoArgs, NoOpts)
if err != nil {
exc := err.(Exception)
if exc.Reason() == Continue {
@ -584,7 +584,7 @@ func (op *whileOp) exec(fm *Frame) Exception {
}
if op.elseOp != nil && !iterated {
return fm.errorp(op, elseBody.Call(fm.fork("while else"), NoArgs, NoOpts))
return fm.errorp(op, elseBody.Call(fm.Fork("while else"), NoArgs, NoOpts))
}
return nil
}
@ -639,7 +639,7 @@ func (op *forOp) exec(fm *Frame) Exception {
errElement = err
return false
}
err = body.Call(fm.fork("for"), NoArgs, NoOpts)
err = body.Call(fm.Fork("for"), NoArgs, NoOpts)
if err != nil {
exc := err.(Exception)
if exc.Reason() == Continue {
@ -661,7 +661,7 @@ func (op *forOp) exec(fm *Frame) Exception {
}
if !iterated && elseBody != nil {
return fm.errorp(op, elseBody.Call(fm.fork("for else"), NoArgs, NoOpts))
return fm.errorp(op, elseBody.Call(fm.Fork("for else"), NoArgs, NoOpts))
}
return nil
}
@ -729,7 +729,7 @@ func (op *tryOp) exec(fm *Frame) Exception {
elseFn := execLambdaOp(fm, op.elseOp)
finally := execLambdaOp(fm, op.finallyOp)
err := body.Call(fm.fork("try body"), NoArgs, NoOpts)
err := body.Call(fm.Fork("try body"), NoArgs, NoOpts)
if err != nil {
if except != nil {
if exceptVar != nil {
@ -738,15 +738,15 @@ func (op *tryOp) exec(fm *Frame) Exception {
return fm.errorp(op.exceptVar, err)
}
}
err = except.Call(fm.fork("try except"), NoArgs, NoOpts)
err = except.Call(fm.Fork("try except"), NoArgs, NoOpts)
}
} else {
if elseFn != nil {
err = elseFn.Call(fm.fork("try else"), NoArgs, NoOpts)
err = elseFn.Call(fm.Fork("try else"), NoArgs, NoOpts)
}
}
if finally != nil {
errFinally := finally.Call(fm.fork("try finally"), NoArgs, NoOpts)
errFinally := finally.Call(fm.Fork("try finally"), NoArgs, NoOpts)
if errFinally != nil {
// TODO: If err is not nil, this discards err. Use something similar
// to pipeline exception to expose both.

View File

@ -15,44 +15,45 @@ import (
"src.elv.sh/pkg/persistent/hash"
)
// A user-defined function in Elvish code. Each closure has its unique identity.
type closure struct {
// Closure is a function defined with Elvish code. Each Closure has its unique
// identity.
type Closure struct {
ArgNames []string
// The index of the rest argument. -1 if there is no rest argument.
RestArg int
OptNames []string
OptDefaults []interface{}
Op effectOp
NewLocal []staticVarInfo
Captured *Ns
SrcMeta parse.Source
DefRange diag.Ranging
op effectOp
newLocal []staticVarInfo
captured *Ns
}
var _ Callable = &closure{}
var _ Callable = &Closure{}
// Kind returns "fn".
func (*closure) Kind() string {
func (*Closure) Kind() string {
return "fn"
}
// Equal compares by address.
func (c *closure) Equal(rhs interface{}) bool {
func (c *Closure) Equal(rhs interface{}) bool {
return c == rhs
}
// Hash returns the hash of the address of the closure.
func (c *closure) Hash() uint32 {
func (c *Closure) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(c))
}
// Repr returns an opaque representation "<closure 0x23333333>".
func (c *closure) Repr(int) string {
func (c *Closure) Repr(int) string {
return fmt.Sprintf("<closure %p>", c)
}
// Call calls a closure.
func (c *closure) Call(fm *Frame, args []interface{}, opts map[string]interface{}) error {
func (c *Closure) Call(fm *Frame, args []interface{}, opts map[string]interface{}) error {
// Check number of arguments.
if c.RestArg != -1 {
if len(args) < len(c.ArgNames)-1 {
@ -93,10 +94,10 @@ func (c *closure) Call(fm *Frame, args []interface{}, opts map[string]interface{
// and ports can be problematic.
// Make upvalue namespace and capture variables.
fm.up = c.Captured
fm.up = c.captured
// Populate local scope with arguments, options, and newly created locals.
localSize := len(c.ArgNames) + len(c.OptNames) + len(c.NewLocal)
localSize := len(c.ArgNames) + len(c.OptNames) + len(c.newLocal)
local := &Ns{make([]vars.Var, localSize), make([]staticVarInfo, localSize)}
for i, name := range c.ArgNames {
@ -129,7 +130,7 @@ func (c *closure) Call(fm *Frame, args []interface{}, opts map[string]interface{
}
offset += len(c.OptNames)
for i, info := range c.NewLocal {
for i, info := range c.newLocal {
local.infos[offset+i] = info
// TODO: Take info.readOnly into account too when creating variable
local.slots[offset+i] = MakeVarFromName(info.name)
@ -138,7 +139,7 @@ func (c *closure) Call(fm *Frame, args []interface{}, opts map[string]interface{
fm.local = local
fm.srcMeta = c.SrcMeta
fm.defers = new([]func(*Frame) Exception)
exc := c.Op.exec(fm)
exc := c.op.exec(fm)
excDefer := fm.runDefers()
// TODO: Combine exc and excDefer if both are not nil
if excDefer != nil && exc == nil {
@ -180,9 +181,9 @@ func (er UnsupportedOptionsError) Error() string {
return fmt.Sprintf("unsupported options: %s", strings.Join(er.Options, ", "))
}
func (c *closure) Fields() vals.StructMap { return closureFields{c} }
func (c *Closure) Fields() vals.StructMap { return closureFields{c} }
type closureFields struct{ c *closure }
type closureFields struct{ c *Closure }
func (closureFields) IsStructMap() {}
@ -196,7 +197,7 @@ func (cf closureFields) OptDefaults() vals.List {
}
func (cf closureFields) Body() string {
r := cf.c.Op.(diag.Ranger).Range()
r := cf.c.op.(diag.Ranger).Range()
return cf.c.SrcMeta.Code[r.From:r.To]
}

View File

@ -72,7 +72,7 @@ func (op *pipelineOp) exec(fm *Frame) Exception {
}
if op.bg {
fm = fm.fork("background job" + op.source)
fm = fm.Fork("background job" + op.source)
fm.intCh = nil
fm.background = true
fm.Evaler.addNumBgJobs(1)
@ -88,7 +88,7 @@ func (op *pipelineOp) exec(fm *Frame) Exception {
// For each form, create a dedicated evalCtx and run asynchronously
for i, formOp := range op.subops {
newFm := fm.fork("[form op]")
newFm := fm.Fork("[form op]")
inputIsPipe := i > 0
outputIsPipe := i < nforms-1
if inputIsPipe {

View File

@ -468,7 +468,7 @@ func (op *lambdaOp) exec(fm *Frame) ([]interface{}, Exception) {
}
optDefaults[i] = defaultValue
}
return []interface{}{&closure{op.argNames, op.restArg, op.optNames, optDefaults, op.subop, op.newLocal, capture, op.srcMeta, op.Range()}}, nil
return []interface{}{&Closure{op.argNames, op.restArg, op.optNames, optDefaults, op.srcMeta, op.Range(), op.subop, op.newLocal, capture}}, nil
}
type mapOp struct {

View File

@ -154,9 +154,9 @@ func linesToChan(r io.Reader, ch chan<- interface{}) {
}
}
// fork returns a modified copy of ec. The ports are forked, and the name is
// Fork returns a modified copy of fm. The ports are forked, and the name is
// changed to the given value. Other fields are copied shallowly.
func (fm *Frame) fork(name string) *Frame {
func (fm *Frame) Fork(name string) *Frame {
newPorts := make([]*Port, len(fm.ports))
for i, p := range fm.ports {
if p != nil {
@ -173,7 +173,7 @@ func (fm *Frame) fork(name string) *Frame {
// A shorthand for forking a frame and setting the output port.
func (fm *Frame) forkWithOutput(name string, p *Port) *Frame {
newFm := fm.fork(name)
newFm := fm.Fork(name)
newFm.ports[1] = p
return newFm
}