github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/closure.go (about) 1 package eval 2 3 import ( 4 "fmt" 5 "sort" 6 "strconv" 7 "strings" 8 "unsafe" 9 10 "src.elv.sh/pkg/diag" 11 "src.elv.sh/pkg/eval/errs" 12 "src.elv.sh/pkg/eval/vals" 13 "src.elv.sh/pkg/eval/vars" 14 "src.elv.sh/pkg/parse" 15 "src.elv.sh/pkg/persistent/hash" 16 ) 17 18 // A user-defined function in Elvish code. Each closure has its unique identity. 19 type closure struct { 20 ArgNames []string 21 // The index of the rest argument. -1 if there is no rest argument. 22 RestArg int 23 OptNames []string 24 OptDefaults []interface{} 25 Op effectOp 26 NewLocal []string 27 Captured *Ns 28 SrcMeta parse.Source 29 DefRange diag.Ranging 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 address. 40 func (c *closure) Equal(rhs interface{}) bool { 41 return c == rhs 42 } 43 44 // Hash returns the hash of the address of the closure. 45 func (c *closure) Hash() uint32 { 46 return hash.Pointer(unsafe.Pointer(c)) 47 } 48 49 // Repr returns an opaque representation "<closure 0x23333333>". 50 func (c *closure) Repr(int) string { 51 return fmt.Sprintf("<closure %p>", c) 52 } 53 54 // Call calls a closure. 55 func (c *closure) Call(fm *Frame, args []interface{}, opts map[string]interface{}) error { 56 // Check number of arguments. 57 if c.RestArg != -1 { 58 if len(args) < len(c.ArgNames)-1 { 59 return errs.ArityMismatch{ 60 What: "arguments here", 61 ValidLow: len(c.ArgNames) - 1, ValidHigh: -1, Actual: len(args)} 62 } 63 } else { 64 if len(args) != len(c.ArgNames) { 65 return errs.ArityMismatch{ 66 What: "arguments here", 67 ValidLow: len(c.ArgNames), ValidHigh: len(c.ArgNames), Actual: len(args)} 68 } 69 } 70 // Check whether all supplied options are supported. This map contains the 71 // subset of keys from opts that can be found in c.OptNames. 72 optSupported := make(map[string]struct{}) 73 for _, name := range c.OptNames { 74 _, ok := opts[name] 75 if ok { 76 optSupported[name] = struct{}{} 77 } 78 } 79 if len(optSupported) < len(opts) { 80 // Report all the options that are not supported. 81 unsupported := make([]string, 0, len(opts)-len(optSupported)) 82 for name := range opts { 83 _, supported := optSupported[name] 84 if !supported { 85 unsupported = append(unsupported, parse.Quote(name)) 86 } 87 } 88 sort.Strings(unsupported) 89 return UnsupportedOptionsError{unsupported} 90 } 91 92 // This Frame is dedicated to the current form, so we can modify it in place. 93 94 // BUG(xiaq): When evaluating closures, async access to global variables 95 // and ports can be problematic. 96 97 // Make upvalue namespace and capture variables. 98 fm.up = c.Captured 99 100 // Populate local scope with arguments, options, and newly created locals. 101 localSize := len(c.ArgNames) + len(c.OptNames) + len(c.NewLocal) 102 local := &Ns{make([]vars.Var, localSize), make([]string, localSize), make([]bool, localSize)} 103 104 for i, name := range c.ArgNames { 105 local.names[i] = name 106 } 107 if c.RestArg == -1 { 108 for i := range c.ArgNames { 109 local.slots[i] = vars.FromInit(args[i]) 110 } 111 } else { 112 for i := 0; i < c.RestArg; i++ { 113 local.slots[i] = vars.FromInit(args[i]) 114 } 115 restOff := len(args) - len(c.ArgNames) 116 local.slots[c.RestArg] = vars.FromInit( 117 vals.MakeList(args[c.RestArg : c.RestArg+restOff+1]...)) 118 for i := c.RestArg + 1; i < len(c.ArgNames); i++ { 119 local.slots[i] = vars.FromInit(args[i+restOff]) 120 } 121 } 122 123 offset := len(c.ArgNames) 124 for i, name := range c.OptNames { 125 v, ok := opts[name] 126 if !ok { 127 v = c.OptDefaults[i] 128 } 129 local.names[offset+i] = name 130 local.slots[offset+i] = vars.FromInit(v) 131 } 132 133 offset += len(c.OptNames) 134 for i, name := range c.NewLocal { 135 local.names[offset+i] = name 136 local.slots[offset+i] = MakeVarFromName(name) 137 } 138 139 fm.local = local 140 fm.srcMeta = c.SrcMeta 141 return c.Op.exec(fm) 142 } 143 144 // MakeVarFromName creates a Var with a suitable type constraint inferred from 145 // the name. 146 func MakeVarFromName(name string) vars.Var { 147 switch { 148 case strings.HasSuffix(name, FnSuffix): 149 val := NewGoFn("nop~", nop) 150 return vars.FromPtr(&val) 151 case strings.HasSuffix(name, NsSuffix): 152 val := (*Ns)(nil) 153 return vars.FromPtr(&val) 154 default: 155 return vars.FromInit(nil) 156 } 157 } 158 159 // UnsupportedOptionsError is an error returned by a closure call when there are 160 // unsupported options. 161 type UnsupportedOptionsError struct { 162 Options []string 163 } 164 165 func (er UnsupportedOptionsError) Error() string { 166 if len(er.Options) == 1 { 167 return fmt.Sprintf("unsupported option: %s", er.Options[0]) 168 } 169 return fmt.Sprintf("unsupported options: %s", strings.Join(er.Options, ", ")) 170 } 171 172 func (c *closure) Fields() vals.StructMap { return closureFields{c} } 173 174 type closureFields struct{ c *closure } 175 176 func (closureFields) IsStructMap() {} 177 178 func (cf closureFields) ArgNames() vals.List { return listOfStrings(cf.c.ArgNames) } 179 func (cf closureFields) RestArg() string { return strconv.Itoa(cf.c.RestArg) } 180 func (cf closureFields) OptNames() vals.List { return listOfStrings(cf.c.OptNames) } 181 func (cf closureFields) Src() parse.Source { return cf.c.SrcMeta } 182 183 func (cf closureFields) OptDefaults() vals.List { 184 return vals.MakeList(cf.c.OptDefaults...) 185 } 186 187 func (cf closureFields) Body() string { 188 r := cf.c.Op.(diag.Ranger).Range() 189 return cf.c.SrcMeta.Code[r.From:r.To] 190 } 191 192 func (cf closureFields) Def() string { 193 return cf.c.SrcMeta.Code[cf.c.DefRange.From:cf.c.DefRange.To] 194 } 195 196 func listOfStrings(ss []string) vals.List { 197 list := vals.EmptyList 198 for _, s := range ss { 199 list = list.Cons(s) 200 } 201 return list 202 }