Add runtime.Field

This commit is contained in:
Anton Medvedev
2022-10-19 23:37:56 +02:00
parent 26975ea2f3
commit 4104ff8967
5 changed files with 45 additions and 25 deletions
+1
View File
@@ -110,6 +110,7 @@ type MemberNode struct {
Optional bool
Deref bool
Index []int
Name string
}
type SliceNode struct {
+5 -3
View File
@@ -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
View File
@@ -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)
+8 -3
View File
@@ -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{} {
+2 -2
View File
@@ -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)])