2016-02-19 05:52:05 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2017-08-31 02:52:27 +08:00
|
|
|
"unsafe"
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
"github.com/elves/elvish/eval/types"
|
2018-01-03 09:13:51 +08:00
|
|
|
"github.com/elves/elvish/eval/vartypes"
|
2018-01-14 23:48:31 +08:00
|
|
|
"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
|
2016-02-26 08:54:27 +08:00
|
|
|
// The name for the rest argument. If empty, the function has fixed arity.
|
2017-08-06 04:27:44 +08:00
|
|
|
RestArg string
|
|
|
|
OptNames []string
|
2018-01-01 04:31:45 +08:00
|
|
|
OptDefaults []types.Value
|
2017-08-06 04:27:44 +08:00
|
|
|
Op Op
|
2017-12-24 07:51:32 +08:00
|
|
|
Captured Ns
|
2018-01-05 12:07:11 +08:00
|
|
|
SrcMeta *Source
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
|
|
|
|
2018-01-01 01:12:02 +08:00
|
|
|
var _ Fn = &Closure{}
|
2016-09-15 05:34:10 +08:00
|
|
|
|
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 {
|
2017-07-14 08:15:14 +08:00
|
|
|
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>".
|
2016-02-20 03:11:31 +08:00
|
|
|
func (c *Closure) Repr(int) string {
|
2016-10-12 02:03:25 +08:00
|
|
|
return fmt.Sprintf("<closure %p>", c)
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call calls a closure.
|
2018-01-21 19:05:36 +08:00
|
|
|
func (c *Closure) Call(ec *Frame, args []types.Value, opts map[string]types.Value) error {
|
2016-02-26 08:54:27 +08:00
|
|
|
if c.RestArg != "" {
|
|
|
|
if len(c.ArgNames) > len(args) {
|
2018-01-21 19:05:36 +08:00
|
|
|
return fmt.Errorf("need %d or more arguments, got %d", len(c.ArgNames), len(args))
|
2016-02-26 08:54:27 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if len(c.ArgNames) != len(args) {
|
2018-01-21 19:05:36 +08:00
|
|
|
return fmt.Errorf("need %d arguments, got %d", len(c.ArgNames), len(args))
|
2016-02-26 08:54:27 +08:00
|
|
|
}
|
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.
|
2017-09-20 20:43:28 +08:00
|
|
|
// TODO(xiaq): Is it safe to simply assign ec.up = c.Captured?
|
2017-12-24 07:51:32 +08:00
|
|
|
ec.up = make(Ns)
|
|
|
|
for name, variable := range c.Captured {
|
|
|
|
ec.up[name] = variable
|
2017-09-20 20:43:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Populate local scope with arguments, possibly a rest argument, and
|
|
|
|
// options.
|
2017-12-24 07:51:32 +08:00
|
|
|
ec.local = make(Ns)
|
2016-02-26 08:54:27 +08:00
|
|
|
for i, name := range c.ArgNames {
|
2018-01-05 06:55:31 +08:00
|
|
|
ec.local[name] = vartypes.NewPtr(args[i])
|
2016-02-26 08:54:27 +08:00
|
|
|
}
|
2017-08-06 01:54:16 +08:00
|
|
|
if c.RestArg != "" {
|
2018-01-05 06:55:31 +08:00
|
|
|
ec.local[c.RestArg] = vartypes.NewPtr(types.MakeList(args[len(c.ArgNames):]...))
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
2018-01-14 23:48:31 +08:00
|
|
|
optUsed := make(map[string]struct{})
|
2017-08-06 04:27:44 +08:00
|
|
|
for i, name := range c.OptNames {
|
|
|
|
v, ok := opts[name]
|
2018-01-14 23:48:31 +08:00
|
|
|
if ok {
|
|
|
|
optUsed[name] = struct{}{}
|
|
|
|
} else {
|
2017-08-06 04:27:44 +08:00
|
|
|
v = c.OptDefaults[i]
|
|
|
|
}
|
2018-01-05 06:55:31 +08:00
|
|
|
ec.local[name] = vartypes.NewPtr(v)
|
2017-08-06 04:27:44 +08:00
|
|
|
}
|
2018-01-14 23:48:31 +08:00
|
|
|
for name := range opts {
|
|
|
|
_, used := optUsed[name]
|
|
|
|
if !used {
|
2018-01-21 19:05:36 +08:00
|
|
|
return fmt.Errorf("unknown option %s", parse.Quote(name))
|
2018-01-14 23:48:31 +08:00
|
|
|
}
|
2016-09-15 06:00:52 +08:00
|
|
|
}
|
2016-02-19 05:52:05 +08:00
|
|
|
|
2016-10-11 22:47:55 +08:00
|
|
|
ec.traceback = ec.addTraceback()
|
|
|
|
|
2018-01-05 12:07:11 +08:00
|
|
|
ec.srcMeta = c.SrcMeta
|
2018-01-21 19:05:36 +08:00
|
|
|
return c.Op.Exec(ec)
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|