Skip to content

Commit 25b5649

Browse files
committed
Add runtime.Field
1 parent 076a63f commit 25b5649

File tree

5 files changed

+45
-25
lines changed

5 files changed

+45
-25
lines changed

ast/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ type MemberNode struct {
110110
Optional bool
111111
Deref bool
112112
Index []int
113+
Name string
113114
}
114115

115116
type SliceNode struct {

checker/checker.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,18 +402,20 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
402402

403403
case reflect.Struct:
404404
if name, ok := node.Property.(*ast.StringNode); ok {
405-
if field, ok := fetchField(base, name.Value); ok {
405+
propertyName := name.Value
406+
if field, ok := fetchField(base, propertyName); ok {
406407
t, c := deref(field.Type)
407408
node.Deref = c
408409
node.Index = field.Index
410+
node.Name = propertyName
409411
return t, info{}
410412
}
411413
if len(v.parents) > 1 {
412414
if _, ok := v.parents[len(v.parents)-2].(*ast.CallNode); ok {
413-
return v.error(node, "type %v has no method %v", base, name.Value)
415+
return v.error(node, "type %v has no method %v", base, propertyName)
414416
}
415417
}
416-
return v.error(node, "type %v has no field %v", base, name.Value)
418+
return v.error(node, "type %v has no field %v", base, propertyName)
417419
}
418420
}
419421

compiler/compiler.go

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package compiler
33
import (
44
"encoding/binary"
55
"fmt"
6+
"github.com/antonmedv/expr/vm/runtime"
67
"math"
78
"reflect"
89

@@ -14,11 +15,11 @@ import (
1415
)
1516

1617
func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err error) {
17-
defer func() {
18-
if r := recover(); r != nil {
19-
err = fmt.Errorf("%v", r)
20-
}
21-
}()
18+
//defer func() {
19+
// if r := recover(); r != nil {
20+
// err = fmt.Errorf("%v", r)
21+
// }
22+
//}()
2223

2324
c := &compiler{
2425
index: make(map[interface{}]uint16),
@@ -77,27 +78,32 @@ func (c *compiler) emitPush(value interface{}) int {
7778
return c.emit(OpPush, c.makeConstant(value)...)
7879
}
7980

80-
func (c *compiler) makeConstant(i interface{}) []byte {
81-
hashable := true
82-
switch reflect.TypeOf(i).Kind() {
83-
case reflect.Slice, reflect.Map:
84-
hashable = false
81+
func (c *compiler) makeConstant(constant interface{}) []byte {
82+
indexable := true
83+
hash := constant
84+
switch reflect.TypeOf(constant).Kind() {
85+
case reflect.Slice, reflect.Map, reflect.Struct:
86+
indexable = false
87+
}
88+
if field, ok := constant.(*runtime.Field); ok {
89+
indexable = true
90+
hash = fmt.Sprintf("%v", field)
8591
}
8692

87-
if hashable {
88-
if p, ok := c.index[i]; ok {
93+
if indexable {
94+
if p, ok := c.index[hash]; ok {
8995
return encode(p)
9096
}
9197
}
9298

93-
c.constants = append(c.constants, i)
99+
c.constants = append(c.constants, constant)
94100
if len(c.constants) > math.MaxUint16 {
95101
panic("exceeded constants max space limit")
96102
}
97103

98104
p := uint16(len(c.constants) - 1)
99-
if hashable {
100-
c.index[i] = p
105+
if indexable {
106+
c.index[hash] = p
101107
}
102108
return encode(p)
103109
}
@@ -179,7 +185,10 @@ func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
179185
if c.mapEnv {
180186
c.emit(OpFetchEnvFast, c.makeConstant(node.Value)...)
181187
} else if len(node.Index) > 0 {
182-
c.emit(OpFetchEnvField, c.makeConstant(node.Index)...)
188+
c.emit(OpFetchEnvField, c.makeConstant(&runtime.Field{
189+
Index: node.Index,
190+
Path: node.Value,
191+
})...)
183192
} else {
184193
c.emit(OpFetchEnv, c.makeConstant(node.Value)...)
185194
}
@@ -415,7 +424,10 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
415424
c.chains[len(c.chains)-1] = append(c.chains[len(c.chains)-1], ph)
416425
}
417426
if len(node.Index) > 0 {
418-
c.emit(OpFetchField, c.makeConstant(node.Index)...)
427+
c.emit(OpFetchField, c.makeConstant(&runtime.Field{
428+
Index: node.Index,
429+
Path: node.Name,
430+
})...)
419431
} else {
420432
c.compile(node.Property)
421433
c.emit(OpFetch)

vm/runtime/runtime.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ func Fetch(from, i interface{}) interface{} {
6666
panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
6767
}
6868

69-
func FetchField(from interface{}, index []int) interface{} {
69+
type Field struct {
70+
Index []int
71+
Path string
72+
}
73+
74+
func FetchField(from interface{}, field *Field) interface{} {
7075
v := reflect.ValueOf(from)
7176
kind := v.Kind()
7277
if kind != reflect.Invalid {
@@ -75,13 +80,13 @@ func FetchField(from interface{}, index []int) interface{} {
7580
kind = v.Kind()
7681
}
7782
if kind == reflect.Struct {
78-
value := v.FieldByIndex(index)
83+
value := v.FieldByIndex(field.Index)
7984
if value.IsValid() && value.CanInterface() {
8085
return value.Interface()
8186
}
8287
}
8388
}
84-
panic(fmt.Sprintf("cannot get field from %T", from))
89+
panic(fmt.Sprintf("cannot get %v from %T", field.Path, from))
8590
}
8691

8792
func Deref(i interface{}) interface{} {

vm/vm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
108108

109109
case OpFetchField:
110110
a := vm.pop()
111-
vm.push(runtime.FetchField(a, vm.constant().([]int)))
111+
vm.push(runtime.FetchField(a, vm.constant().(*runtime.Field)))
112112

113113
case OpFetchEnv:
114114
vm.push(runtime.Fetch(env, vm.constant()))
115115

116116
case OpFetchEnvField:
117-
vm.push(runtime.FetchField(env, vm.constant().([]int)))
117+
vm.push(runtime.FetchField(env, vm.constant().(*runtime.Field)))
118118

119119
case OpFetchEnvFast:
120120
vm.push(env.(map[string]interface{})[vm.constant().(string)])

0 commit comments

Comments
 (0)