github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/cmds/elvish/eval/builtin_fn.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 8 "github.com/u-root/u-root/cmds/elvish/eval/vals" 9 "github.com/u-root/u-root/cmds/elvish/hash" 10 ) 11 12 var ErrArgs = errors.New("args error") 13 14 // BuiltinFn uses reflection to wrap arbitrary Go functions into Elvish 15 // functions. 16 // 17 // Parameters are passed following these rules: 18 // 19 // 1. If the first parameter of function has type *Frame, it gets the current 20 // call frame. 21 // 22 // 2. If (possibly after a *Frame parameter) the first parameter has type 23 // RawOptions, it gets a map of options. If the function has not declared an 24 // RawOptions parameter but is passed options, an error is thrown. 25 // 26 // 3. If the last parameter is non-variadic and has type Inputs, it represents 27 // an optional parameter that contains the input to this function. If the 28 // argument is not supplied, the input channel of the Frame will be used to 29 // supply the inputs. 30 // 31 // 4. Other parameters are converted using elvToGo. 32 // 33 // Return values go to the channel part of the stdout port, after being 34 // converted using goToElv. If the last return value has type error and is not 35 // nil, it is turned into an exception and no ouputting happens. If the last 36 // return value is a nil error, it is ignored. 37 type BuiltinFn struct { 38 name string 39 impl interface{} 40 41 // Type information of impl. 42 43 frame bool 44 options bool 45 inputs bool 46 // Type of "normal" (non-Frame, non-Options, non-variadic) arguments. 47 normalArgs []reflect.Type 48 // Type of variadic arguments, nil if function is non-variadic 49 variadicArg reflect.Type 50 } 51 52 var _ Callable = &BuiltinFn{} 53 54 type ( 55 Inputs func(func(interface{})) 56 ) 57 58 var ( 59 frameType = reflect.TypeOf((*Frame)(nil)) 60 rawOptionsType = reflect.TypeOf(RawOptions(nil)) 61 inputsType = reflect.TypeOf(Inputs(nil)) 62 ) 63 64 // NewBuiltinFn creates a new ReflectBuiltinFn instance. 65 func NewBuiltinFn(name string, impl interface{}) *BuiltinFn { 66 implType := reflect.TypeOf(impl) 67 b := &BuiltinFn{name: name, impl: impl} 68 69 i := 0 70 if i < implType.NumIn() && implType.In(i) == frameType { 71 b.frame = true 72 i++ 73 } 74 if i < implType.NumIn() && implType.In(i) == rawOptionsType { 75 b.options = true 76 i++ 77 } 78 for ; i < implType.NumIn(); i++ { 79 paramType := implType.In(i) 80 if i == implType.NumIn()-1 { 81 if implType.IsVariadic() { 82 b.variadicArg = paramType.Elem() 83 break 84 } else if paramType == inputsType { 85 b.inputs = true 86 break 87 } 88 } 89 b.normalArgs = append(b.normalArgs, paramType) 90 } 91 return b 92 } 93 94 // Kind returns "fn". 95 func (*BuiltinFn) Kind() string { 96 return "fn" 97 } 98 99 // Equal compares identity. 100 func (b *BuiltinFn) Equal(rhs interface{}) bool { 101 return b == rhs 102 } 103 104 // Hash hashes the address. 105 func (b *BuiltinFn) Hash() uint32 { 106 return hash.Hash(b) 107 } 108 109 // Repr returns an opaque representation "<builtin $name>". 110 func (b *BuiltinFn) Repr(int) string { 111 return "<builtin " + b.name + ">" 112 } 113 114 // error(nil) is treated as nil by reflect.TypeOf, so we first get the type of 115 // *error and use Elem to obtain type of error. 116 var errorType = reflect.TypeOf((*error)(nil)).Elem() 117 118 var errNoOptions = errors.New("function does not accept any options") 119 120 // Call calls the implementation using reflection. 121 func (b *BuiltinFn) Call(f *Frame, args []interface{}, opts map[string]interface{}) error { 122 if b.variadicArg != nil { 123 if len(args) < len(b.normalArgs) { 124 return fmt.Errorf("want %d or more arguments, got %d", 125 len(b.normalArgs), len(args)) 126 } 127 } else if b.inputs { 128 if len(args) != len(b.normalArgs) && len(args) != len(b.normalArgs)+1 { 129 return fmt.Errorf("want %d or %d arguments, got %d", 130 len(b.normalArgs), len(b.normalArgs)+1, len(args)) 131 } 132 } else if len(args) != len(b.normalArgs) { 133 return fmt.Errorf("want %d arguments, got %d", len(b.normalArgs), len(args)) 134 } 135 if !b.options && len(opts) > 0 { 136 return errNoOptions 137 } 138 139 var in []reflect.Value 140 if b.frame { 141 in = append(in, reflect.ValueOf(f)) 142 } 143 if b.options { 144 in = append(in, reflect.ValueOf(opts)) 145 } 146 for i, arg := range args { 147 var typ reflect.Type 148 if i < len(b.normalArgs) { 149 typ = b.normalArgs[i] 150 } else if b.variadicArg != nil { 151 typ = b.variadicArg 152 } else if b.inputs { 153 break // Handled after the loop 154 } else { 155 panic("impossible") 156 } 157 ptr := reflect.New(typ) 158 err := vals.ScanToGo(arg, ptr.Interface()) 159 if err != nil { 160 return fmt.Errorf("wrong type of %d'th argument: %v", i+1, err) 161 } 162 in = append(in, ptr.Elem()) 163 } 164 165 if b.inputs { 166 var inputs Inputs 167 if len(args) == len(b.normalArgs) { 168 inputs = Inputs(f.IterateInputs) 169 } else { 170 // Wrap an iterable argument in Inputs. 171 iterable := args[len(args)-1] 172 inputs = Inputs(func(f func(interface{})) { 173 err := vals.Iterate(iterable, func(v interface{}) bool { 174 f(v) 175 return true 176 }) 177 maybeThrow(err) 178 }) 179 } 180 in = append(in, reflect.ValueOf(inputs)) 181 } 182 183 outs := reflect.ValueOf(b.impl).Call(in) 184 185 if len(outs) > 0 && outs[len(outs)-1].Type() == errorType { 186 err := outs[len(outs)-1].Interface() 187 if err != nil { 188 return err.(error) 189 } 190 outs = outs[:len(outs)-1] 191 } 192 193 for _, out := range outs { 194 f.OutputChan() <- vals.FromGo(out.Interface()) 195 } 196 return nil 197 }