From 2780ad9f670658c0e4b7a9f9c5cf5816a0217e7b Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 11:38:04 +0800 Subject: [PATCH] feat: call function with two argument --- builtin/function.go | 6 ++++-- parser.go | 32 ++++++++++++++++++++++++++------ parser_test.go | 19 +++++++++++++++---- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/builtin/function.go b/builtin/function.go index 897c5078..8049a113 100644 --- a/builtin/function.go +++ b/builtin/function.go @@ -1,13 +1,15 @@ package builtin import ( + "math" "math/rand" "time" ) var Functions = map[string]interface{}{ - "sleep": sleep, - "gen_random_string": genRandomString, + "sleep": sleep, // call with one argument + "gen_random_string": genRandomString, // call with one argument + "max": math.Max, // call with two arguments } func sleep(nSecs int) { diff --git a/parser.go b/parser.go index c27a8e30..661d67c5 100644 --- a/parser.go +++ b/parser.go @@ -169,9 +169,9 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s return mergedVariables } -// callFunction call function with arguments +// callFunc call function with arguments // 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] if !ok { // function not found @@ -184,25 +184,45 @@ func callFunction(funcName string, arguments []interface{}) (interface{}, error) 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)) 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) } log.Printf("[callFunction] func: %v, input arguments: %v", funcName, arguments) resultValues := funcValue.Call(argumentsValue) - log.Printf("[callFunction] output results: %v", resultValues) + log.Printf("[callFunction] output values: %v", resultValues) - if len(resultValues) == 0 { - return nil, nil - } else if len(resultValues) > 1 { + if len(resultValues) > 1 { // function should return at most one value err := fmt.Errorf("function %s should return at most one value", funcName) log.Printf("[callFunction] error: %s", err.Error()) return nil, err } + // no return value + if len(resultValues) == 0 { + return nil, nil + } + + // return one value // convert reflect.Value to interface{} result := resultValues[0].Interface() + log.Printf("[callFunction] output result: %+v(%T)", result, result) return result, nil } diff --git a/parser_test.go b/parser_test.go index 674971ea..8e4e0540 100644 --- a/parser_test.go +++ b/parser_test.go @@ -283,9 +283,9 @@ func TestMergeVariables(t *testing.T) { func TestCallFunction(t *testing.T) { // call function without arguments funcName := "sleep" - params := []interface{}{1} + arguments := []interface{}{1} timeStart := time.Now() - _, err := callFunction(funcName, params) + _, err := callFunc(funcName, arguments) if !assert.Nil(t, err) { t.Fail() } @@ -295,12 +295,23 @@ func TestCallFunction(t *testing.T) { // call function with one argument funcName = "gen_random_string" - params = []interface{}{10} - result, err := callFunction(funcName, params) + arguments = []interface{}{10} + result, err := callFunc(funcName, arguments) if !assert.Nil(t, err) { t.Fail() } if !assert.Equal(t, 10, len(result.(string))) { 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() + } }