Skip to content

Commit 1c7ad66

Browse files
feat: Operator overload from Function
1 parent 47eae7e commit 1c7ad66

File tree

5 files changed

+47
-5
lines changed

5 files changed

+47
-5
lines changed

conf/config.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func CreateNew() *Config {
3131
Operators: make(map[string][]string),
3232
ConstFns: make(map[string]reflect.Value),
3333
Functions: make(map[string]*builtin.Function),
34+
Types: make(TypesTable),
3435
Optimize: true,
3536
}
3637
for _, f := range builtin.Functions {
@@ -87,10 +88,15 @@ func (c *Config) Check() {
8788
panic(fmt.Errorf("function %s for %s operator does not exist in the environment", fn, operator))
8889
}
8990
requiredNumIn := 2
91+
requiredNumOut := 1
9092
if fnType.Method {
9193
requiredNumIn = 3 // As first argument of method is receiver.
9294
}
93-
if fnType.Type.NumIn() != requiredNumIn || fnType.Type.NumOut() != 1 {
95+
if fnType.Type.NumIn() == 1 && fnType.Type.In(0).Kind() == reflect.Slice {
96+
requiredNumIn = 1
97+
requiredNumOut = 2
98+
}
99+
if fnType.Type.NumIn() != requiredNumIn || fnType.Type.NumOut() != requiredNumOut {
94100
panic(fmt.Errorf("function %s for %s operator does not have a correct signature", fn, operator))
95101
}
96102
}

conf/operators.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ func FindSuitableOperatorOverload(fns []string, types TypesTable, l, r reflect.T
1717
if fnType.Method {
1818
firstInIndex = 1 // As first argument to method is receiver.
1919
}
20-
firstArgType := fnType.Type.In(firstInIndex)
21-
secondArgType := fnType.Type.In(firstInIndex + 1)
20+
var firstArgType reflect.Type
21+
var secondArgType reflect.Type
22+
23+
if fnType.Type.NumIn() == 1 && fnType.Type.In(0).Kind() == reflect.Slice {
24+
firstArgType = fnType.Type.In(0).Elem()
25+
secondArgType = fnType.Type.In(0).Elem()
26+
} else {
27+
firstArgType = fnType.Type.In(firstInIndex)
28+
secondArgType = fnType.Type.In(firstInIndex + 1)
29+
}
2230

2331
firstArgumentFit := l == firstArgType || (firstArgType.Kind() == reflect.Interface && (l == nil || l.Implements(firstArgType)))
2432
secondArgumentFit := r == secondArgType || (secondArgType.Kind() == reflect.Interface && (r == nil || r.Implements(secondArgType)))

conf/types_table.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ type TypesTable map[string]Tag
2121
// If map is passed, all items will be treated as variables
2222
// (key as name, value as type).
2323
func CreateTypesTable(i interface{}) TypesTable {
24+
types := make(TypesTable)
2425
if i == nil {
25-
return nil
26+
return types
2627
}
2728

28-
types := make(TypesTable)
2929
v := reflect.ValueOf(i)
3030
t := reflect.TypeOf(i)
3131

expr.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ func Function(name string, fn func(params ...interface{}) (interface{}, error),
127127
Func: fn,
128128
Types: ts,
129129
}
130+
c.Types[name] = conf.Tag{
131+
Type: reflect.TypeOf(fn),
132+
}
130133
}
131134
}
132135

test/operator/operator_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,28 @@ func TestOperator_interface(t *testing.T) {
5454
require.NoError(t, err)
5555
require.Equal(t, true, output)
5656
}
57+
58+
func TestOperator_Function(t *testing.T) {
59+
env := map[string]interface{}{
60+
"foo": Value{1},
61+
"bar": Value{2},
62+
}
63+
64+
program, err := expr.Compile(
65+
`foo + bar`,
66+
expr.Env(env),
67+
expr.Operator("+", "Add"),
68+
expr.Function("Add", func(args ...interface{}) (interface{}, error) {
69+
return args[0].(Value).Int + args[1].(Value).Int, nil
70+
}),
71+
)
72+
require.NoError(t, err)
73+
74+
output, err := expr.Run(program, env)
75+
require.NoError(t, err)
76+
require.Equal(t, 3, output)
77+
}
78+
79+
type Value struct {
80+
Int int
81+
}

0 commit comments

Comments
 (0)