mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Add OpCallTyped (#269)
This commit is contained in:
@@ -128,6 +128,7 @@ type CallNode struct {
|
||||
base
|
||||
Callee Node
|
||||
Arguments []Node
|
||||
Typed int
|
||||
Fast bool
|
||||
}
|
||||
|
||||
|
||||
+32
-1
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/antonmedv/expr/conf"
|
||||
"github.com/antonmedv/expr/file"
|
||||
"github.com/antonmedv/expr/parser"
|
||||
"github.com/antonmedv/expr/vm"
|
||||
)
|
||||
|
||||
func Check(tree *parser.Tree, config *conf.Config) (t reflect.Type, err error) {
|
||||
@@ -498,7 +499,7 @@ func (v *visitor) CallNode(node *ast.CallNode) (reflect.Type, info) {
|
||||
}
|
||||
|
||||
// checkFunc checks func arguments and returns "return type" of func or method.
|
||||
func (v *visitor) checkFunc(fn reflect.Type, method bool, node ast.Node, name string, arguments []ast.Node) (reflect.Type, info) {
|
||||
func (v *visitor) checkFunc(fn reflect.Type, method bool, node *ast.CallNode, name string, arguments []ast.Node) (reflect.Type, info) {
|
||||
if isAny(fn) {
|
||||
return anyType, info{}
|
||||
}
|
||||
@@ -564,6 +565,36 @@ func (v *visitor) checkFunc(fn reflect.Type, method bool, node ast.Node, name st
|
||||
}
|
||||
}
|
||||
|
||||
if !fn.IsVariadic() {
|
||||
funcTypes:
|
||||
for i := range vm.FuncTypes {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
typed := reflect.ValueOf(vm.FuncTypes[i]).Elem().Type()
|
||||
if typed.Kind() != reflect.Func {
|
||||
continue
|
||||
}
|
||||
if typed.NumOut() != fn.NumOut() {
|
||||
continue
|
||||
}
|
||||
for j := 0; j < typed.NumOut(); j++ {
|
||||
if typed.Out(j) != fn.Out(j) {
|
||||
continue funcTypes
|
||||
}
|
||||
}
|
||||
if typed.NumIn() != len(arguments) {
|
||||
continue
|
||||
}
|
||||
for j, arg := range arguments {
|
||||
if typed.In(j) != arg.Type() {
|
||||
continue funcTypes
|
||||
}
|
||||
}
|
||||
node.Typed = i
|
||||
}
|
||||
}
|
||||
|
||||
return fn.Out(0), info{}
|
||||
}
|
||||
|
||||
|
||||
@@ -523,12 +523,15 @@ func (c *compiler) CallNode(node *ast.CallNode) {
|
||||
for _, arg := range node.Arguments {
|
||||
c.compile(arg)
|
||||
}
|
||||
op := OpCall
|
||||
if node.Fast {
|
||||
op = OpCallFast
|
||||
}
|
||||
c.compile(node.Callee)
|
||||
c.emit(op, len(node.Arguments))
|
||||
if node.Typed > 0 {
|
||||
c.emit(OpCallTyped, node.Typed)
|
||||
return
|
||||
} else if node.Fast {
|
||||
c.emit(OpCallFast, len(node.Arguments))
|
||||
} else {
|
||||
c.emit(OpCall, len(node.Arguments))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/template"
|
||||
. "time"
|
||||
)
|
||||
|
||||
// Keep sorted.
|
||||
var types = []interface{}{
|
||||
nil,
|
||||
new(func() Duration),
|
||||
new(func() Month),
|
||||
new(func() Time),
|
||||
new(func() Weekday),
|
||||
new(func() []byte),
|
||||
new(func() []interface{}),
|
||||
new(func() bool),
|
||||
new(func() byte),
|
||||
new(func() float64),
|
||||
new(func() int),
|
||||
new(func() int64),
|
||||
new(func() interface{}),
|
||||
new(func() map[string]interface{}),
|
||||
new(func() rune),
|
||||
new(func() string),
|
||||
new(func() uint),
|
||||
new(func() uint64),
|
||||
new(func(Duration) Duration),
|
||||
new(func(Duration) Time),
|
||||
new(func(Time) Duration),
|
||||
new(func(Time) bool),
|
||||
new(func([]interface{}, string) string),
|
||||
new(func([]string, string) string),
|
||||
new(func(bool) bool),
|
||||
new(func(bool) float64),
|
||||
new(func(bool) int),
|
||||
new(func(bool) string),
|
||||
new(func(float64) bool),
|
||||
new(func(float64) float64),
|
||||
new(func(float64) int),
|
||||
new(func(float64) string),
|
||||
new(func(int) bool),
|
||||
new(func(int) float64),
|
||||
new(func(int) int),
|
||||
new(func(int) string),
|
||||
new(func(int, int) int),
|
||||
new(func(int, int) string),
|
||||
new(func(int64) Time),
|
||||
new(func(string) []string),
|
||||
new(func(string) bool),
|
||||
new(func(string) float64),
|
||||
new(func(string) int),
|
||||
new(func(string) string),
|
||||
new(func(string, byte) int),
|
||||
new(func(string, int) int),
|
||||
new(func(string, rune) int),
|
||||
new(func(string, string) bool),
|
||||
new(func(string, string) string),
|
||||
}
|
||||
|
||||
func main() {
|
||||
data := struct {
|
||||
Index string
|
||||
Code string
|
||||
}{}
|
||||
|
||||
for i, t := range types {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
fn := reflect.ValueOf(t).Elem().Type()
|
||||
data.Index += fmt.Sprintf("%v: new(%v),\n", i, fn)
|
||||
data.Code += fmt.Sprintf("case %d:\n", i)
|
||||
args := make([]string, fn.NumIn())
|
||||
for j := fn.NumIn() - 1; j >= 0; j-- {
|
||||
data.Code += fmt.Sprintf("arg%v := vm.pop().(%v)\n", j+1, fn.In(j))
|
||||
args[j] = fmt.Sprintf("arg%v", j+1)
|
||||
}
|
||||
data.Code += fmt.Sprintf("return fn.(%v)(%v)\n", fn, strings.Join(args, ", "))
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
err := template.Must(
|
||||
template.New("func_types").
|
||||
Parse(source),
|
||||
).Execute(&b, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
formatted, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(string(formatted))
|
||||
}
|
||||
|
||||
const source = `// Code generated by vm/func_types/main.go. DO NOT EDIT.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var FuncTypes = []interface{}{
|
||||
{{ .Index }}
|
||||
}
|
||||
|
||||
func (vm *VM) call(fn interface{}, kind int) interface{} {
|
||||
switch kind {
|
||||
{{ .Code }}
|
||||
}
|
||||
panic(fmt.Sprintf("unknown function kind (%v)", kind))
|
||||
}
|
||||
`
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
// Code generated by vm/func_types/main.go. DO NOT EDIT.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var FuncTypes = []interface{}{
|
||||
1: new(func() time.Duration),
|
||||
2: new(func() time.Month),
|
||||
3: new(func() time.Time),
|
||||
4: new(func() time.Weekday),
|
||||
5: new(func() []uint8),
|
||||
6: new(func() []interface{}),
|
||||
7: new(func() bool),
|
||||
8: new(func() uint8),
|
||||
9: new(func() float64),
|
||||
10: new(func() int),
|
||||
11: new(func() int64),
|
||||
12: new(func() interface{}),
|
||||
13: new(func() map[string]interface{}),
|
||||
14: new(func() int32),
|
||||
15: new(func() string),
|
||||
16: new(func() uint),
|
||||
17: new(func() uint64),
|
||||
18: new(func(time.Duration) time.Duration),
|
||||
19: new(func(time.Duration) time.Time),
|
||||
20: new(func(time.Time) time.Duration),
|
||||
21: new(func(time.Time) bool),
|
||||
22: new(func([]interface{}, string) string),
|
||||
23: new(func([]string, string) string),
|
||||
24: new(func(bool) bool),
|
||||
25: new(func(bool) float64),
|
||||
26: new(func(bool) int),
|
||||
27: new(func(bool) string),
|
||||
28: new(func(float64) bool),
|
||||
29: new(func(float64) float64),
|
||||
30: new(func(float64) int),
|
||||
31: new(func(float64) string),
|
||||
32: new(func(int) bool),
|
||||
33: new(func(int) float64),
|
||||
34: new(func(int) int),
|
||||
35: new(func(int) string),
|
||||
36: new(func(int, int) int),
|
||||
37: new(func(int, int) string),
|
||||
38: new(func(int64) time.Time),
|
||||
39: new(func(string) []string),
|
||||
40: new(func(string) bool),
|
||||
41: new(func(string) float64),
|
||||
42: new(func(string) int),
|
||||
43: new(func(string) string),
|
||||
44: new(func(string, uint8) int),
|
||||
45: new(func(string, int) int),
|
||||
46: new(func(string, int32) int),
|
||||
47: new(func(string, string) bool),
|
||||
48: new(func(string, string) string),
|
||||
}
|
||||
|
||||
func (vm *VM) call(fn interface{}, kind int) interface{} {
|
||||
switch kind {
|
||||
case 1:
|
||||
return fn.(func() time.Duration)()
|
||||
case 2:
|
||||
return fn.(func() time.Month)()
|
||||
case 3:
|
||||
return fn.(func() time.Time)()
|
||||
case 4:
|
||||
return fn.(func() time.Weekday)()
|
||||
case 5:
|
||||
return fn.(func() []uint8)()
|
||||
case 6:
|
||||
return fn.(func() []interface{})()
|
||||
case 7:
|
||||
return fn.(func() bool)()
|
||||
case 8:
|
||||
return fn.(func() uint8)()
|
||||
case 9:
|
||||
return fn.(func() float64)()
|
||||
case 10:
|
||||
return fn.(func() int)()
|
||||
case 11:
|
||||
return fn.(func() int64)()
|
||||
case 12:
|
||||
return fn.(func() interface{})()
|
||||
case 13:
|
||||
return fn.(func() map[string]interface{})()
|
||||
case 14:
|
||||
return fn.(func() int32)()
|
||||
case 15:
|
||||
return fn.(func() string)()
|
||||
case 16:
|
||||
return fn.(func() uint)()
|
||||
case 17:
|
||||
return fn.(func() uint64)()
|
||||
case 18:
|
||||
arg1 := vm.pop().(time.Duration)
|
||||
return fn.(func(time.Duration) time.Duration)(arg1)
|
||||
case 19:
|
||||
arg1 := vm.pop().(time.Duration)
|
||||
return fn.(func(time.Duration) time.Time)(arg1)
|
||||
case 20:
|
||||
arg1 := vm.pop().(time.Time)
|
||||
return fn.(func(time.Time) time.Duration)(arg1)
|
||||
case 21:
|
||||
arg1 := vm.pop().(time.Time)
|
||||
return fn.(func(time.Time) bool)(arg1)
|
||||
case 22:
|
||||
arg2 := vm.pop().(string)
|
||||
arg1 := vm.pop().([]interface{})
|
||||
return fn.(func([]interface{}, string) string)(arg1, arg2)
|
||||
case 23:
|
||||
arg2 := vm.pop().(string)
|
||||
arg1 := vm.pop().([]string)
|
||||
return fn.(func([]string, string) string)(arg1, arg2)
|
||||
case 24:
|
||||
arg1 := vm.pop().(bool)
|
||||
return fn.(func(bool) bool)(arg1)
|
||||
case 25:
|
||||
arg1 := vm.pop().(bool)
|
||||
return fn.(func(bool) float64)(arg1)
|
||||
case 26:
|
||||
arg1 := vm.pop().(bool)
|
||||
return fn.(func(bool) int)(arg1)
|
||||
case 27:
|
||||
arg1 := vm.pop().(bool)
|
||||
return fn.(func(bool) string)(arg1)
|
||||
case 28:
|
||||
arg1 := vm.pop().(float64)
|
||||
return fn.(func(float64) bool)(arg1)
|
||||
case 29:
|
||||
arg1 := vm.pop().(float64)
|
||||
return fn.(func(float64) float64)(arg1)
|
||||
case 30:
|
||||
arg1 := vm.pop().(float64)
|
||||
return fn.(func(float64) int)(arg1)
|
||||
case 31:
|
||||
arg1 := vm.pop().(float64)
|
||||
return fn.(func(float64) string)(arg1)
|
||||
case 32:
|
||||
arg1 := vm.pop().(int)
|
||||
return fn.(func(int) bool)(arg1)
|
||||
case 33:
|
||||
arg1 := vm.pop().(int)
|
||||
return fn.(func(int) float64)(arg1)
|
||||
case 34:
|
||||
arg1 := vm.pop().(int)
|
||||
return fn.(func(int) int)(arg1)
|
||||
case 35:
|
||||
arg1 := vm.pop().(int)
|
||||
return fn.(func(int) string)(arg1)
|
||||
case 36:
|
||||
arg2 := vm.pop().(int)
|
||||
arg1 := vm.pop().(int)
|
||||
return fn.(func(int, int) int)(arg1, arg2)
|
||||
case 37:
|
||||
arg2 := vm.pop().(int)
|
||||
arg1 := vm.pop().(int)
|
||||
return fn.(func(int, int) string)(arg1, arg2)
|
||||
case 38:
|
||||
arg1 := vm.pop().(int64)
|
||||
return fn.(func(int64) time.Time)(arg1)
|
||||
case 39:
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string) []string)(arg1)
|
||||
case 40:
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string) bool)(arg1)
|
||||
case 41:
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string) float64)(arg1)
|
||||
case 42:
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string) int)(arg1)
|
||||
case 43:
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string) string)(arg1)
|
||||
case 44:
|
||||
arg2 := vm.pop().(uint8)
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string, uint8) int)(arg1, arg2)
|
||||
case 45:
|
||||
arg2 := vm.pop().(int)
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string, int) int)(arg1, arg2)
|
||||
case 46:
|
||||
arg2 := vm.pop().(int32)
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string, int32) int)(arg1, arg2)
|
||||
case 47:
|
||||
arg2 := vm.pop().(string)
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string, string) bool)(arg1, arg2)
|
||||
case 48:
|
||||
arg2 := vm.pop().(string)
|
||||
arg1 := vm.pop().(string)
|
||||
return fn.(func(string, string) string)(arg1, arg2)
|
||||
|
||||
}
|
||||
panic(fmt.Sprintf("unknown function kind (%v)", kind))
|
||||
}
|
||||
@@ -48,6 +48,7 @@ const (
|
||||
OpSlice
|
||||
OpCall
|
||||
OpCallFast
|
||||
OpCallTyped
|
||||
OpArray
|
||||
OpMap
|
||||
OpLen
|
||||
|
||||
@@ -194,6 +194,9 @@ func (program *Program) Disassemble() string {
|
||||
case OpCallFast:
|
||||
argument("OpCallFast")
|
||||
|
||||
case OpCallTyped:
|
||||
argument("OpCallTyped")
|
||||
|
||||
case OpArray:
|
||||
code("OpArray")
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package vm
|
||||
|
||||
//go:generate sh -c "go run ./func_types > ./generated.go"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
@@ -314,6 +316,11 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
|
||||
}
|
||||
vm.push(fn(in...))
|
||||
|
||||
case OpCallTyped:
|
||||
fn := vm.pop()
|
||||
out := vm.call(fn, arg)
|
||||
vm.push(out)
|
||||
|
||||
case OpArray:
|
||||
size := vm.pop().(int)
|
||||
array := make([]interface{}, size)
|
||||
|
||||
Reference in New Issue
Block a user