/
closure.go
131 lines (114 loc) · 3.24 KB
/
closure.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package eval
import (
"errors"
"fmt"
"unsafe"
"github.com/elves/elvish/eval/vals"
"github.com/elves/elvish/eval/vars"
"github.com/elves/elvish/parse"
"github.com/xiaq/persistent/hash"
)
// ErrArityMismatch is thrown by a closure when the number of arguments the user
// supplies does not match with what is required.
var ErrArityMismatch = errors.New("arity mismatch")
// Closure is a closure defined in elvish script.
type Closure struct {
ArgNames []string
// The name for the rest argument. If empty, the function has fixed arity.
RestArg string
OptNames []string
OptDefaults []interface{}
Op Op
Captured Ns
SrcMeta *Source
DefBegint int
DefEnd int
}
var _ Callable = &Closure{}
// Kind returns "fn".
func (*Closure) Kind() string {
return "fn"
}
// Equal compares by identity.
func (c *Closure) Equal(rhs interface{}) bool {
return c == rhs
}
func (c *Closure) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(c))
}
// Repr returns an opaque representation "<closure 0x23333333>".
func (c *Closure) Repr(int) string {
return fmt.Sprintf("<closure %p>", c)
}
func (c *Closure) Index(k interface{}) (interface{}, bool) {
switch k {
case "arg-names":
return vals.MakeStringList(c.ArgNames...), true
case "rest-arg":
return c.RestArg, true
case "opt-names":
return vals.MakeStringList(c.OptNames...), true
case "opt-defaults":
return vals.MakeList(c.OptDefaults...), true
case "body":
return c.SrcMeta.code[c.Op.Begin:c.Op.End], true
case "def":
return c.SrcMeta.code[c.DefBegint:c.DefEnd], true
case "src":
return c.SrcMeta, true
}
return nil, false
}
func (c *Closure) IterateKeys(f func(interface{}) bool) {
vals.Feed(f, "arg-names", "rest-arg",
"opt-names", "opt-defaults", "body", "def", "src")
}
// Call calls a closure.
func (c *Closure) Call(fm *Frame, args []interface{}, opts map[string]interface{}) 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))
}
}
// 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?
fm.up = make(Ns)
for name, variable := range c.Captured {
fm.up[name] = variable
}
// Populate local scope with arguments, possibly a rest argument, and
// options.
fm.local = make(Ns)
for i, name := range c.ArgNames {
fm.local[name] = vars.NewAnyWithInit(args[i])
}
if c.RestArg != "" {
fm.local[c.RestArg] = vars.NewAnyWithInit(vals.MakeList(args[len(c.ArgNames):]...))
}
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]
}
fm.local[name] = vars.NewAnyWithInit(v)
}
for name := range opts {
_, used := optUsed[name]
if !used {
return fmt.Errorf("unknown option %s", parse.Quote(name))
}
}
fm.traceback = fm.addTraceback()
fm.srcMeta = c.SrcMeta
return c.Op.Exec(fm)
}