elvish/eval/closure.go

105 lines
2.5 KiB
Go
Raw Normal View History

2016-02-19 05:52:05 +08:00
package eval
import (
"errors"
"fmt"
2017-08-31 02:52:27 +08:00
"unsafe"
"github.com/elves/elvish/eval/types"
"github.com/elves/elvish/eval/vartypes"
"github.com/elves/elvish/parse"
2017-08-31 02:52:27 +08:00
"github.com/xiaq/persistent/hash"
2016-02-19 05:52:05 +08:00
)
2017-06-11 07:04:53 +08:00
// ErrArityMismatch is thrown by a closure when the number of arguments the user
// supplies does not match with what is required.
2016-02-19 05:52:05 +08:00
var ErrArityMismatch = errors.New("arity mismatch")
2016-03-02 08:35:13 +08:00
// Closure is a closure defined in elvish script.
2016-02-19 05:52:05 +08:00
type Closure struct {
ArgNames []string
// The name for the rest argument. If empty, the function has fixed arity.
RestArg string
OptNames []string
OptDefaults []types.Value
Op Op
Captured Ns
SrcMeta *Source
2016-02-19 05:52:05 +08:00
}
2018-01-01 01:12:02 +08:00
var _ Fn = &Closure{}
2017-06-11 07:04:53 +08:00
// Kind returns "fn".
2016-02-19 05:52:05 +08:00
func (*Closure) Kind() string {
return "fn"
}
2017-08-31 01:47:50 +08:00
// Equal compares by identity.
func (c *Closure) Equal(rhs interface{}) bool {
return c == rhs
}
2017-08-31 02:52:27 +08:00
func (c *Closure) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(c))
}
2017-06-11 07:04:53 +08:00
// Repr returns an opaque representation "<closure 0x23333333>".
func (c *Closure) Repr(int) string {
return fmt.Sprintf("<closure %p>", c)
2016-02-19 05:52:05 +08:00
}
// Call calls a closure.
func (c *Closure) Call(ec *Frame, args []types.Value, opts map[string]types.Value) error {
if c.RestArg != "" {
if len(c.ArgNames) > len(args) {
return fmt.Errorf("need %d or more arguments, got %d", len(c.ArgNames), len(args))
}
} else {
if len(c.ArgNames) != len(args) {
return fmt.Errorf("need %d arguments, got %d", len(c.ArgNames), len(args))
}
2016-02-19 05:52:05 +08:00
}
// This evalCtx is dedicated to the current form, so we modify it in place.
// BUG(xiaq): When evaluating closures, async access to global variables
// and ports can be problematic.
// Make upvalue namespace and capture variables.
// TODO(xiaq): Is it safe to simply assign ec.up = c.Captured?
ec.up = make(Ns)
for name, variable := range c.Captured {
ec.up[name] = variable
}
// Populate local scope with arguments, possibly a rest argument, and
// options.
ec.local = make(Ns)
for i, name := range c.ArgNames {
ec.local[name] = vartypes.NewPtr(args[i])
}
if c.RestArg != "" {
ec.local[c.RestArg] = vartypes.NewPtr(types.MakeList(args[len(c.ArgNames):]...))
2016-02-19 05:52:05 +08:00
}
optUsed := make(map[string]struct{})
for i, name := range c.OptNames {
v, ok := opts[name]
if ok {
optUsed[name] = struct{}{}
} else {
v = c.OptDefaults[i]
}
ec.local[name] = vartypes.NewPtr(v)
}
for name := range opts {
_, used := optUsed[name]
if !used {
return fmt.Errorf("unknown option %s", parse.Quote(name))
}
2016-09-15 06:00:52 +08:00
}
2016-02-19 05:52:05 +08:00
ec.traceback = ec.addTraceback()
ec.srcMeta = c.SrcMeta
return c.Op.Exec(ec)
2016-02-19 05:52:05 +08:00
}