Skip to content

Commit

Permalink
PaesslerAG#22 fix generic map key types
Browse files Browse the repository at this point in the history
  • Loading branch information
JanErik Keller committed Apr 18, 2019
1 parent 4dca502 commit 4f1cfd4
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 52 deletions.
113 changes: 62 additions & 51 deletions evaluable.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,14 @@ func constant(value interface{}) Evaluable {
}

//Var Evaluable represents value at given path.
//It supports:
//It supports with default language VariableSelector:
// map[interface{}]interface{},
// map[string]interface{},
// []interface{} and
// struct fields
// map[string]interface{} and
// []interface{} and via reflect
// struct fields,
// struct methods,
// slices and
// map with int or string key.
func (p *Parser) Var(path ...Evaluable) Evaluable {
if p.Language.selector == nil {
return variable(path)
Expand Down Expand Up @@ -123,62 +126,70 @@ func variable(path Evaluables) Evaluable {
continue
}
default:
vv := reflect.ValueOf(o)
vvElem := vv

// if this is a pointer, resolve it.
if vv.Kind() == reflect.Ptr {
vvElem = vv.Elem()
}

if vvElem.Kind() == reflect.Map {
vvElem = vv.MapIndex(reflect.ValueOf(k))

if vvElem.Kind() == reflect.Ptr {
vvElem = vvElem.Elem()
}

if vvElem.IsValid() {
v = vvElem.Interface()

continue
}
var ok bool
v, ok = reflectSelect(k, o)
if !ok {
return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
}
}
}
return v, nil
}
}

if vvElem.Kind() == reflect.Slice {
if i, err := strconv.Atoi(k); err == nil && i >= 0 && vv.Len() > i {
vvElem = vv.Index(i)
func reflectSelect(key string, value interface{}) (selection interface{}, ok bool) {
vv := reflect.ValueOf(value)
vvElem := resolvePotentialPointer(vv)

if vvElem.Kind() == reflect.Ptr {
vvElem = vvElem.Elem()
}
switch vvElem.Kind() {
case reflect.Map:
mapKey, ok := reflectConvertTo(vv.Type().Key().Kind(), key)
if !ok {
return nil, false
}

v = vvElem.Interface()
vvElem = vv.MapIndex(reflect.ValueOf(mapKey))
vvElem = resolvePotentialPointer(vvElem)

continue
}
}
if vvElem.IsValid() {
return vvElem.Interface(), true
}
case reflect.Slice:
if i, err := strconv.Atoi(key); err == nil && i >= 0 && vv.Len() > i {
vvElem = resolvePotentialPointer(vv.Index(i))
return vvElem.Interface(), true
}
case reflect.Struct:
field := vvElem.FieldByName(key)
if field.IsValid() {
return field.Interface(), true
}

if vvElem.Kind() != reflect.Struct {
break
}
method := vv.MethodByName(key)
if method.IsValid() {
return method.Interface(), true
}
}
return nil, false
}

field := vvElem.FieldByName(k)
if field.IsValid() {
v = field.Interface()
continue
}
func resolvePotentialPointer(value reflect.Value) reflect.Value {
if value.Kind() == reflect.Ptr {
return value.Elem()
}
return value
}

method := vv.MethodByName(k)
if method.IsValid() {
v = method.Interface()
continue
}
}
return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
func reflectConvertTo(k reflect.Kind, value string) (interface{}, bool) {
switch k {
case reflect.String:
return value, true
case reflect.Int:
if i, err := strconv.Atoi(value); err == nil {
return i, true
}
return v, nil
}
return nil, false
}

func (*Parser) callFunc(fun function, args ...Evaluable) Evaluable {
Expand Down Expand Up @@ -233,7 +244,7 @@ func (*Parser) callEvaluable(fullname string, fun Evaluable, args ...Evaluable)
}

errorInterface := reflect.TypeOf((*error)(nil)).Elem()
if len(r) > 0 && ff.Type().Out(len(r) - 1).Implements(errorInterface) {
if len(r) > 0 && ff.Type().Out(len(r)-1).Implements(errorInterface) {
if r[len(r)-1] != nil {
err = r[len(r)-1].(error)
}
Expand Down
10 changes: 9 additions & 1 deletion gval_parameterized_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,13 +577,21 @@ func TestParameterized(t *testing.T) {
want: "hello world!",
},
{
name: "int keys",
name: "map[int]int",
expression: `a[0] + a[2]`,
parameter: map[string]interface{}{
"a": map[int]int{0: 1, 2: 1},
},
want: 2.,
},
{
name: "map[int]string",
expression: `a[0] * a[2]`,
parameter: map[string]interface{}{
"a": map[int]string{0: "1", 2: "1"},
},
want: 1.,
},
},
t,
)
Expand Down

0 comments on commit 4f1cfd4

Please sign in to comment.