-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcallable.go
91 lines (73 loc) · 1.71 KB
/
callable.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
package expr
import (
"errors"
"fmt"
"reflect"
)
type Callable interface {
Call(arguments []interface{}) interface{}
ArgNum() int
}
type PrintFunc struct{}
func (PrintFunc) Call(_ *Interpreter, args []interface{}) interface{} {
fmt.Printf("%+v", args)
return nil
}
func (PrintFunc) ArgNum() int { return 1 }
type GoFunc struct {
argNum int
call func(arguments []interface{}) interface{}
}
func (f *GoFunc) Call(args []interface{}) interface{} {
return f.call(args)
}
func (f *GoFunc) ArgNum() int {
return f.argNum
}
func (e *Environment) DefineGoFunc(name string, f interface{}) error {
t := reflect.TypeOf(f)
if t.Kind() != reflect.Func {
return errors.New("not a function")
}
v := reflect.ValueOf(f)
numIn := t.NumIn()
numOut := t.NumOut()
if numOut > 1 {
return errors.New("too many return values")
}
gf := &GoFunc{
argNum: numIn,
call: func(arguments []interface{}) interface{} {
in := make([]reflect.Value, 0, numIn)
for i, inputArg := range arguments {
argDef := t.In(i)
inputVal := reflect.ValueOf(inputArg)
converted, ok := TryConvert(inputVal, argDef)
if !ok {
return RuntimeError{msg: fmt.Sprintf("%s argument[%d] '%+v' %T is not compatible for %+v",
name, i, inputArg, inputArg, argDef)}
}
in = append(in, converted)
}
results := v.Call(in)
if len(results) == 0 {
return nil
}
return results[0].Interface()
},
}
e.Define(name, gf)
return nil
}
// go1.17 以后可以使用 reflect.Value.CanConvert() 判断
func TryConvert(v reflect.Value, t reflect.Type) (converted reflect.Value, ok bool) {
defer func() {
r := recover()
if r == nil {
return
}
ok = false
}()
converted = v.Convert(t)
return converted, true
}