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