mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Add runtime.Field
This commit is contained in:
@@ -110,6 +110,7 @@ type MemberNode struct {
|
||||
Optional bool
|
||||
Deref bool
|
||||
Index []int
|
||||
Name string
|
||||
}
|
||||
|
||||
type SliceNode struct {
|
||||
|
||||
+5
-3
@@ -402,18 +402,20 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
|
||||
|
||||
case reflect.Struct:
|
||||
if name, ok := node.Property.(*ast.StringNode); ok {
|
||||
if field, ok := fetchField(base, name.Value); ok {
|
||||
propertyName := name.Value
|
||||
if field, ok := fetchField(base, propertyName); ok {
|
||||
t, c := deref(field.Type)
|
||||
node.Deref = c
|
||||
node.Index = field.Index
|
||||
node.Name = propertyName
|
||||
return t, info{}
|
||||
}
|
||||
if len(v.parents) > 1 {
|
||||
if _, ok := v.parents[len(v.parents)-2].(*ast.CallNode); ok {
|
||||
return v.error(node, "type %v has no method %v", base, name.Value)
|
||||
return v.error(node, "type %v has no method %v", base, propertyName)
|
||||
}
|
||||
}
|
||||
return v.error(node, "type %v has no field %v", base, name.Value)
|
||||
return v.error(node, "type %v has no field %v", base, propertyName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-17
@@ -3,6 +3,7 @@ package compiler
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/antonmedv/expr/vm/runtime"
|
||||
"math"
|
||||
"reflect"
|
||||
|
||||
@@ -14,11 +15,11 @@ import (
|
||||
)
|
||||
|
||||
func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}()
|
||||
//defer func() {
|
||||
// if r := recover(); r != nil {
|
||||
// err = fmt.Errorf("%v", r)
|
||||
// }
|
||||
//}()
|
||||
|
||||
c := &compiler{
|
||||
index: make(map[interface{}]uint16),
|
||||
@@ -77,27 +78,32 @@ func (c *compiler) emitPush(value interface{}) int {
|
||||
return c.emit(OpPush, c.makeConstant(value)...)
|
||||
}
|
||||
|
||||
func (c *compiler) makeConstant(i interface{}) []byte {
|
||||
hashable := true
|
||||
switch reflect.TypeOf(i).Kind() {
|
||||
case reflect.Slice, reflect.Map:
|
||||
hashable = false
|
||||
func (c *compiler) makeConstant(constant interface{}) []byte {
|
||||
indexable := true
|
||||
hash := constant
|
||||
switch reflect.TypeOf(constant).Kind() {
|
||||
case reflect.Slice, reflect.Map, reflect.Struct:
|
||||
indexable = false
|
||||
}
|
||||
if field, ok := constant.(*runtime.Field); ok {
|
||||
indexable = true
|
||||
hash = fmt.Sprintf("%v", field)
|
||||
}
|
||||
|
||||
if hashable {
|
||||
if p, ok := c.index[i]; ok {
|
||||
if indexable {
|
||||
if p, ok := c.index[hash]; ok {
|
||||
return encode(p)
|
||||
}
|
||||
}
|
||||
|
||||
c.constants = append(c.constants, i)
|
||||
c.constants = append(c.constants, constant)
|
||||
if len(c.constants) > math.MaxUint16 {
|
||||
panic("exceeded constants max space limit")
|
||||
}
|
||||
|
||||
p := uint16(len(c.constants) - 1)
|
||||
if hashable {
|
||||
c.index[i] = p
|
||||
if indexable {
|
||||
c.index[hash] = p
|
||||
}
|
||||
return encode(p)
|
||||
}
|
||||
@@ -179,7 +185,10 @@ func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
|
||||
if c.mapEnv {
|
||||
c.emit(OpFetchEnvFast, c.makeConstant(node.Value)...)
|
||||
} else if len(node.Index) > 0 {
|
||||
c.emit(OpFetchEnvField, c.makeConstant(node.Index)...)
|
||||
c.emit(OpFetchEnvField, c.makeConstant(&runtime.Field{
|
||||
Index: node.Index,
|
||||
Path: node.Value,
|
||||
})...)
|
||||
} else {
|
||||
c.emit(OpFetchEnv, c.makeConstant(node.Value)...)
|
||||
}
|
||||
@@ -415,7 +424,10 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
|
||||
c.chains[len(c.chains)-1] = append(c.chains[len(c.chains)-1], ph)
|
||||
}
|
||||
if len(node.Index) > 0 {
|
||||
c.emit(OpFetchField, c.makeConstant(node.Index)...)
|
||||
c.emit(OpFetchField, c.makeConstant(&runtime.Field{
|
||||
Index: node.Index,
|
||||
Path: node.Name,
|
||||
})...)
|
||||
} else {
|
||||
c.compile(node.Property)
|
||||
c.emit(OpFetch)
|
||||
|
||||
@@ -66,7 +66,12 @@ func Fetch(from, i interface{}) interface{} {
|
||||
panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
|
||||
}
|
||||
|
||||
func FetchField(from interface{}, index []int) interface{} {
|
||||
type Field struct {
|
||||
Index []int
|
||||
Path string
|
||||
}
|
||||
|
||||
func FetchField(from interface{}, field *Field) interface{} {
|
||||
v := reflect.ValueOf(from)
|
||||
kind := v.Kind()
|
||||
if kind != reflect.Invalid {
|
||||
@@ -75,13 +80,13 @@ func FetchField(from interface{}, index []int) interface{} {
|
||||
kind = v.Kind()
|
||||
}
|
||||
if kind == reflect.Struct {
|
||||
value := v.FieldByIndex(index)
|
||||
value := v.FieldByIndex(field.Index)
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("cannot get field from %T", from))
|
||||
panic(fmt.Sprintf("cannot get %v from %T", field.Path, from))
|
||||
}
|
||||
|
||||
func Deref(i interface{}) interface{} {
|
||||
|
||||
@@ -108,13 +108,13 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
|
||||
|
||||
case OpFetchField:
|
||||
a := vm.pop()
|
||||
vm.push(runtime.FetchField(a, vm.constant().([]int)))
|
||||
vm.push(runtime.FetchField(a, vm.constant().(*runtime.Field)))
|
||||
|
||||
case OpFetchEnv:
|
||||
vm.push(runtime.Fetch(env, vm.constant()))
|
||||
|
||||
case OpFetchEnvField:
|
||||
vm.push(runtime.FetchField(env, vm.constant().([]int)))
|
||||
vm.push(runtime.FetchField(env, vm.constant().(*runtime.Field)))
|
||||
|
||||
case OpFetchEnvFast:
|
||||
vm.push(env.(map[string]interface{})[vm.constant().(string)])
|
||||
|
||||
Reference in New Issue
Block a user