feat: call function with two argument

This commit is contained in:
debugtalk
2021-10-03 11:38:04 +08:00
parent b32a7a32b6
commit eca8af71f1
3 changed files with 45 additions and 12 deletions
+4 -2
View File
@@ -1,13 +1,15 @@
package builtin package builtin
import ( import (
"math"
"math/rand" "math/rand"
"time" "time"
) )
var Functions = map[string]interface{}{ var Functions = map[string]interface{}{
"sleep": sleep, "sleep": sleep, // call with one argument
"gen_random_string": genRandomString, "gen_random_string": genRandomString, // call with one argument
"max": math.Max, // call with two arguments
} }
func sleep(nSecs int) { func sleep(nSecs int) {
+26 -6
View File
@@ -169,9 +169,9 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s
return mergedVariables return mergedVariables
} }
// callFunction call function with arguments // callFunc call function with arguments
// only support return at most one result value // only support return at most one result value
func callFunction(funcName string, arguments []interface{}) (interface{}, error) { func callFunc(funcName string, arguments []interface{}) (interface{}, error) {
function, ok := builtin.Functions[funcName] function, ok := builtin.Functions[funcName]
if !ok { if !ok {
// function not found // function not found
@@ -184,25 +184,45 @@ func callFunction(funcName string, arguments []interface{}) (interface{}, error)
return nil, fmt.Errorf("function %s is invalid", funcName) return nil, fmt.Errorf("function %s is invalid", funcName)
} }
if funcValue.Type().NumIn() != len(arguments) {
// function arguments not match
return nil, fmt.Errorf("function %s arguments number not match", funcName)
}
argumentsValue := make([]reflect.Value, len(arguments)) argumentsValue := make([]reflect.Value, len(arguments))
for index, argument := range arguments { for index, argument := range arguments {
// ensure each argument type match
expectArgumentType := funcValue.Type().In(index)
actualArgumentType := reflect.TypeOf(argument)
if expectArgumentType != actualArgumentType {
// function argument type not match
err := fmt.Errorf("function %s argument %d type not match, expect %v, actual %v",
funcName, index, expectArgumentType, actualArgumentType)
log.Printf("[callFunction] error: %s", err.Error())
return nil, err
}
argumentsValue[index] = reflect.ValueOf(argument) argumentsValue[index] = reflect.ValueOf(argument)
} }
log.Printf("[callFunction] func: %v, input arguments: %v", funcName, arguments) log.Printf("[callFunction] func: %v, input arguments: %v", funcName, arguments)
resultValues := funcValue.Call(argumentsValue) resultValues := funcValue.Call(argumentsValue)
log.Printf("[callFunction] output results: %v", resultValues) log.Printf("[callFunction] output values: %v", resultValues)
if len(resultValues) == 0 { if len(resultValues) > 1 {
return nil, nil
} else if len(resultValues) > 1 {
// function should return at most one value // function should return at most one value
err := fmt.Errorf("function %s should return at most one value", funcName) err := fmt.Errorf("function %s should return at most one value", funcName)
log.Printf("[callFunction] error: %s", err.Error()) log.Printf("[callFunction] error: %s", err.Error())
return nil, err return nil, err
} }
// no return value
if len(resultValues) == 0 {
return nil, nil
}
// return one value
// convert reflect.Value to interface{} // convert reflect.Value to interface{}
result := resultValues[0].Interface() result := resultValues[0].Interface()
log.Printf("[callFunction] output result: %+v(%T)", result, result)
return result, nil return result, nil
} }
+15 -4
View File
@@ -283,9 +283,9 @@ func TestMergeVariables(t *testing.T) {
func TestCallFunction(t *testing.T) { func TestCallFunction(t *testing.T) {
// call function without arguments // call function without arguments
funcName := "sleep" funcName := "sleep"
params := []interface{}{1} arguments := []interface{}{1}
timeStart := time.Now() timeStart := time.Now()
_, err := callFunction(funcName, params) _, err := callFunc(funcName, arguments)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fail() t.Fail()
} }
@@ -295,12 +295,23 @@ func TestCallFunction(t *testing.T) {
// call function with one argument // call function with one argument
funcName = "gen_random_string" funcName = "gen_random_string"
params = []interface{}{10} arguments = []interface{}{10}
result, err := callFunction(funcName, params) result, err := callFunc(funcName, arguments)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fail() t.Fail()
} }
if !assert.Equal(t, 10, len(result.(string))) { if !assert.Equal(t, 10, len(result.(string))) {
t.Fail() t.Fail()
} }
// call function with two argument
funcName = "max"
arguments = []interface{}{float64(10), 9.99}
result, err = callFunc(funcName, arguments)
if !assert.Nil(t, err) {
t.Fail()
}
if !assert.Equal(t, float64(10), result.(float64)) {
t.Fail()
}
} }