Skip to content

Commit

Permalink
Fix working with struct's method
Browse files Browse the repository at this point in the history
  • Loading branch information
antonmedv committed Oct 10, 2018
1 parent 0c9427d commit 4439fd8
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 26 deletions.
18 changes: 11 additions & 7 deletions eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,19 +586,19 @@ func TestEval_panic(t *testing.T) {
}

func TestEval_method(t *testing.T) {
env := testEnv{
env := &testEnv{
Hello: "hello",
World: testWorld{
name: []string{"w", "o", "r", "l", "d"},
},
testVersion: testVersion{
testVersion: &testVersion{
version: 2,
},
}

input := `Title(Hello) ~ ' ' ~ (CompareVersion(1, 3) ? World.String() : '')`
input := `Title(Hello) ~ Empty() ~ (CompareVersion(1, 3) ? World.String() : '')`

node, err := expr.Parse(input)
node, err := expr.Parse(input, expr.Env(&testEnv{}))
fmt.Printf("%#v\n", node)
if err != nil {
t.Fatal(err)
Expand All @@ -619,7 +619,7 @@ type testVersion struct {
version float64
}

func (c testVersion) CompareVersion(min, max float64) bool {
func (c *testVersion) CompareVersion(min, max float64) bool {
return min < c.version && c.version < max
}

Expand All @@ -632,11 +632,15 @@ func (w testWorld) String() string {
}

type testEnv struct {
testVersion
*testVersion
Hello string
World testWorld
}

func (e testEnv) Title(s string) string {
func (e *testEnv) Title(s string) string {
return strings.Title(s)
}

func (e *testEnv) Empty() string {
return " "
}
27 changes: 15 additions & 12 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,22 @@ func (p *parser) createTypesTable(i interface{}) typesTable {
v := reflect.ValueOf(i)
t := reflect.TypeOf(i)

t = dereference(t)
if t == nil {
return types
d := t
if t.Kind() == reflect.Ptr {
d = t.Elem()
}

switch t.Kind() {
switch d.Kind() {
case reflect.Struct:
types = p.fromStruct(t)
types = p.fieldsFromStruct(d)

// Methods of struct should be gathered from original struct with pointer,
// as methods maybe declared on pointer receiver. Also this method retrieves
// all embedded structs methods as well, no need to recursion.
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
types[m.Name] = m.Type
}

case reflect.Map:
for _, key := range v.MapKeys() {
Expand All @@ -161,7 +169,7 @@ func (p *parser) createTypesTable(i interface{}) typesTable {
return types
}

func (p *parser) fromStruct(t reflect.Type) typesTable {
func (p *parser) fieldsFromStruct(t reflect.Type) typesTable {
types := make(typesTable)
t = dereference(t)
if t == nil {
Expand All @@ -174,18 +182,13 @@ func (p *parser) fromStruct(t reflect.Type) typesTable {
f := t.Field(i)

if f.Anonymous {
for name, typ := range p.fromStruct(f.Type) {
for name, typ := range p.fieldsFromStruct(f.Type) {
types[name] = typ
}
}

types[f.Name] = f.Type
}

for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
types[m.Name] = m.Type
}
}

return types
Expand Down
14 changes: 7 additions & 7 deletions runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,14 @@ func extract(val interface{}, i interface{}) (interface{}, bool) {

func getFunc(val interface{}, i interface{}) (interface{}, bool) {
v := reflect.ValueOf(val)
switch v.Kind() {
d := v
if v.Kind() == reflect.Ptr {
d = v.Elem()
}

switch d.Kind() {
case reflect.Map:
value := v.MapIndex(reflect.ValueOf(i))
value := d.MapIndex(reflect.ValueOf(i))
if value.IsValid() && value.CanInterface() {
return value.Interface(), true
}
Expand All @@ -119,11 +124,6 @@ func getFunc(val interface{}, i interface{}) (interface{}, bool) {
if value.IsValid() && value.CanInterface() {
return value.Interface(), true
}
case reflect.Ptr:
value := v.Elem()
if value.IsValid() && value.CanInterface() {
return getFunc(value.Interface(), i)
}
}
return nil, false
}
Expand Down

0 comments on commit 4439fd8

Please sign in to comment.