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