From 1b2cdd207a75432314f4780d4b0e20bec40c005b Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 19:46:09 +0800 Subject: [PATCH] feat: eval literal --- go.mod | 1 + go.sum | 2 ++ parser.go | 28 ++++++++++++++++++++++++++-- parser_test.go | 25 +++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index df60a9f1..a0392e52 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/imroc/req v0.3.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/maja42/goval v1.2.1 // indirect github.com/myzhan/boomer v1.6.0 github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/shirou/gopsutil v3.21.8+incompatible // indirect diff --git a/go.sum b/go.sum index 8c81c378..5ebb7ba7 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Ca github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/maja42/goval v1.2.1 h1:fyEgzddqPgCZsKcFLk4C6SdCHyEaAHYvtZG4mGzQOHU= +github.com/maja42/goval v1.2.1/go.mod h1:42LU+BQXL/veE9jnTTUOSj38GRmOTSThYSXRVodI5J4= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0= diff --git a/parser.go b/parser.go index ca10c30b..c47be9f3 100644 --- a/parser.go +++ b/parser.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/httprunner/httpboomer/builtin" + "github.com/maja42/goval" ) func parseStep(step IStep, config *TConfig) *TStep { @@ -77,13 +78,15 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa } const ( - regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore - regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore + regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore + regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore + regexNumber = `-?\d+(\.\d+)?` // match number, e.g. 123, -123, 1.23, -1.23 ) var ( regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var regexCompileFunction = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\(([\$\w\.\-/\s=,]*)\)\}`, regexFunctionName)) // parse ${func1($a, $b)} + regexCompileNumber = regexp.MustCompile(regexNumber) // parse number ) // parseString parse string with variables @@ -226,3 +229,24 @@ func callFunc(funcName string, arguments ...interface{}) (interface{}, error) { log.Printf("[callFunction] output result: %+v(%T)", result, result) return result, nil } + +var eval = goval.NewEvaluator() + +// literalEval parse string to number if possible +// e.g. "123" => 123 +// "1.23" => 1.23 +// "abc" => "abc" +// "$var" => "$var" +func literalEval(raw string) (interface{}, error) { + // check if raw is a number + if !regexCompileNumber.Match([]byte(raw)) { + return raw, nil + } + + // eval string to number + result, err := eval.Evaluate(raw, nil, nil) + if err != nil { + return raw, err + } + return result, nil +} diff --git a/parser_test.go b/parser_test.go index b1702625..92190bf9 100644 --- a/parser_test.go +++ b/parser_test.go @@ -315,3 +315,28 @@ func TestCallFunction(t *testing.T) { t.Fail() } } + +func TestLiteralEval(t *testing.T) { + testData := []struct { + expr string + expect interface{} + }{ + {"123", 123}, + {"1.23", 1.23}, + {"-123", -123}, + {"-1.23", -1.23}, + {"abc", "abc"}, + {"$var", "$var"}, + {"", ""}, + } + + for _, data := range testData { + value, err := literalEval(data.expr) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, value) { + t.Fail() + } + } +}