From 7749d16a5949ef4e55de78a6d06d1a14a2671d05 Mon Sep 17 00:00:00 2001 From: sado Date: Sat, 21 May 2022 16:32:35 +0800 Subject: [PATCH] update readme and unit test and example test --- README.md | 11 +++-- kit/rule/README.md | 93 ++++++++++++++++++++++++++++++++++-- kit/rule/collection.go | 15 ++++++ kit/rule/custom_func.go | 9 +--- kit/rule/example_test.go | 55 +++++++++++++++++---- kit/rule/parser.go | 6 +-- kit/rule/parser_test.go | 68 ++++++-------------------- kit/rule/rule_func_test.go | 22 ++++----- kit/rule/rule_params_test.go | 53 +++++++++++++++++++- kit/rule/token_base.go | 24 ++++------ kit/rule/token_bit.go | 10 ++-- kit/rule/token_comparable.go | 45 ++++++++++++++--- kit/rule/token_interface.go | 22 +++++---- kit/rule/token_logic.go | 4 +- kit/rule/token_prefix.go | 8 ++-- kit/rule/tooken_compute.go | 49 ++++++++++++------- 16 files changed, 343 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index eef35cd..a631bc1 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ func test() { - [x] [**ast rule engine**](https://github.com/sado0823/go-kitx/tree/master/kit/rule) -__supported operator__ +__supported operator__ * **comparator**: `>` `>=` `<` `<=` `==` @@ -36,10 +36,15 @@ __supported operator__ * **logic**: `&&` `||` -* **others**: `(` `)` `,` `func`(do func call with build in function and custom function) +* **func call**: `(` `)` `,` `func`(do func call with build in function and custom function) -* **params type**: `Ident` `Number` `String` `Bool` (DO Not support `array` `func` `struct`) +* **params type**: `Ident` `Number` `String` `Bool` `array`, `struct` (DO Not support `func` ) +* **recursive params call with `.`**: `map.mapKey.mapKey.arrayIndex.structFiledName` (foo.bar.2.Name) + +* Link + * [See Example Here]() + * [Check Unit Test Here]() ```go // example diff --git a/kit/rule/README.md b/kit/rule/README.md index a140f34..48390a5 100644 --- a/kit/rule/README.md +++ b/kit/rule/README.md @@ -17,12 +17,15 @@ __supported operator__ * **logic**: `&&` `||` -* **others**: `(` `)` `,` `func`(do func call with build in function and custom function) - -* **params type**: `Ident` `Number` `String` `Bool` (DO Not support `array` `func` `struct`) +* **func call**: `(` `)` `,` `func`(do func call with build in function and custom function) +* **params type**: `Ident` `Number` `String` `Bool` `array`, `struct` (DO Not support `func` ) +* **recursive params call with `.`**: `map.mapKey.mapKey.arrayIndex.structFiledName` (foo.bar.2.Name) +* Link + * [See Example Here]() + * [Check Unit Test Here]() ##### ExampleDo ```go import ( @@ -102,7 +105,7 @@ func ExampleWithCustomFn() { params, /* custom func `strlen` return args[0]'s count with float64 type */ - rule.WithCustomFn("strlen", func(arguments ...interface{}) (interface{}, error) { + rule.WithCustomFn("strlen", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { if len(arguments) == 0 { return 0, nil } @@ -110,7 +113,7 @@ func ExampleWithCustomFn() { }), /*custom func `isTrue` return if args[0] is true with bool type*/ - rule.WithCustomFn("isTrue", func(arguments ...interface{}) (interface{}, error) { + rule.WithCustomFn("isTrue", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { if len(arguments) == 0 { return 0, nil } @@ -126,4 +129,84 @@ func ExampleWithCustomFn() { // Output: // true } +``` + +##### ExampleWithFullFunctional +```go +import ( + "context" + "fmt" + "unicode/utf8" + + "github.com/sado0823/go-kitx/kit/rule" +) + +func ExampleWithCustomFn() { + type Child struct { + Name string + Age int + IsVIP bool + Map map[string]int + Nested *Child + } + type User struct { + Name string + Age int + IsVIP bool + Nil interface{} + Children []Child + } + + params := &User{ + Name: "foo", + Age: 18, + IsVIP: true, + Nil: nil, + Children: []Child{ + { + // 0 + Name: "child0", Age: 0, IsVIP: false, Map: map[string]int{"child0": 0}, Nested: &Child{Name: "child0-child"}, + }, + { + // 1 + Name: "child1", Age: 1, IsVIP: true, Map: map[string]int{"child1": 1}, Nested: &Child{}, + }, + }, + } + + value, err := rule.Do( + context.Background(), + `Name == "foo" && + (Name + "bar" == "foobar") && + (Age == 17 || Age == 18) && + (Age + 1 == 19) && + func in(Name,2,"foo",1) && + func strlen("abc") == 3 && + func isVIP() && + Children.1.Name == "child1" && + Children.1.Map.child1 == 1 && + Children.0.Nested.Name == "child0-child"`, + params, + /* custom func `strlen` return args[0]'s count with float64 type */ + rule.WithCustomFn("strlen", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { + if len(arguments) == 0 { + return 0, nil + } + return float64(utf8.RuneCount([]byte(arguments[0].(string)))), nil + }), + /*custom func `isVIP` return if evalParam.IsVIP is true with bool type*/ + rule.WithCustomFn("isVIP", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { + userCurrent := evalParam.(*User) + return userCurrent.IsVIP == true, nil + }), + ) + if err != nil { + fmt.Println(err) + } + + fmt.Print(value) + + // Output: + // true +} ``` \ No newline at end of file diff --git a/kit/rule/collection.go b/kit/rule/collection.go index f93c10c..76ad365 100644 --- a/kit/rule/collection.go +++ b/kit/rule/collection.go @@ -5,6 +5,21 @@ import ( "strconv" ) +func typeEqual(x, y interface{}) (xT, yT reflect.Type, ok bool) { + xT = reflect.TypeOf(x) + yT = reflect.TypeOf(y) + + return xT, yT, xT.Kind() == yT.Kind() +} + +func isString(value interface{}) bool { + switch value.(type) { + case string: + return true + } + return false +} + func convertToFloat(o interface{}) (float64, bool) { if i, ok := o.(float64); ok { return i, true diff --git a/kit/rule/custom_func.go b/kit/rule/custom_func.go index 35de8da..4b468db 100644 --- a/kit/rule/custom_func.go +++ b/kit/rule/custom_func.go @@ -5,14 +5,11 @@ import ( "reflect" ) -type CustomFn func(arguments ...interface{}) (interface{}, error) +type CustomFn func(evalParam interface{}, arguments ...interface{}) (interface{}, error) var _buildInCustomFn = map[string]CustomFn{ // func(inValue,arr[0],arr[1]) - "in": func(arguments ...interface{}) (interface{}, error) { - logger.Println("build func [in], len args=", len(arguments)) - logger.Println(arguments...) - + "in": func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { if len(arguments) == 0 { return false, fmt.Errorf("no args with func `in`") } @@ -26,9 +23,7 @@ var _buildInCustomFn = map[string]CustomFn{ ) arr = append(arr, arguments[1:]...) - logger.Printf("key=%v, key_type=%T \n", key, key) for _, arg := range arr { - logger.Printf("arg=%v, arg_type=%T \n", arg, arg) if reflect.DeepEqual(key, arg) { return true, nil } diff --git a/kit/rule/example_test.go b/kit/rule/example_test.go index 3d46409..3803830 100644 --- a/kit/rule/example_test.go +++ b/kit/rule/example_test.go @@ -43,25 +43,62 @@ func ExampleNew() { } func ExampleWithCustomFn() { - params := map[string]interface{}{"foo": 1} + type Child struct { + Name string + Age int + IsVIP bool + Map map[string]int + Nested *Child + } + type User struct { + Name string + Age int + IsVIP bool + Nil interface{} + Children []Child + } + + params := &User{ + Name: "foo", + Age: 18, + IsVIP: true, + Nil: nil, + Children: []Child{ + { + // 0 + Name: "child0", Age: 0, IsVIP: false, Map: map[string]int{"child0": 0}, Nested: &Child{Name: "child0-child"}, + }, + { + // 1 + Name: "child1", Age: 1, IsVIP: true, Map: map[string]int{"child1": 1}, Nested: &Child{}, + }, + }, + } value, err := rule.Do( context.Background(), - `func in(foo,2,"abc",1) && func strlen("abc") == 3 && func isTrue(true) && func isTrue(false) == false`, + `Name == "foo" && +(Name + "bar" == "foobar") && +(Age == 17 || Age == 18) && +(Age + 1 == 19) && +func in(Name,2,"foo",1) && +func strlen("abc") == 3 && +func isVIP() && +Children.1.Name == "child1" && +Children.1.Map.child1 == 1 && +Children.0.Nested.Name == "child0-child"`, params, /* custom func `strlen` return args[0]'s count with float64 type */ - rule.WithCustomFn("strlen", func(arguments ...interface{}) (interface{}, error) { + rule.WithCustomFn("strlen", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { if len(arguments) == 0 { return 0, nil } return float64(utf8.RuneCount([]byte(arguments[0].(string)))), nil }), - /*custom func `isTrue` return if args[0] is true with bool type*/ - rule.WithCustomFn("isTrue", func(arguments ...interface{}) (interface{}, error) { - if len(arguments) == 0 { - return 0, nil - } - return arguments[0].(bool) == true, nil + /*custom func `isVIP` return if evalParam.IsVIP is true with bool type*/ + rule.WithCustomFn("isVIP", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { + userCurrent := evalParam.(*User) + return userCurrent.IsVIP == true, nil }), ) if err != nil { diff --git a/kit/rule/parser.go b/kit/rule/parser.go index abae916..2eb8a36 100644 --- a/kit/rule/parser.go +++ b/kit/rule/parser.go @@ -74,7 +74,7 @@ func Check(ctx context.Context, expr string, options ...WithOption) (err error) return err } -func Do(ctx context.Context, expr string, params map[string]interface{}, options ...WithOption) (interface{}, error) { +func Do(ctx context.Context, expr string, params interface{}, options ...WithOption) (interface{}, error) { parser, err := New(ctx, expr, options...) if err != nil { return nil, err @@ -102,7 +102,7 @@ func New(ctx context.Context, expr string, options ...WithOption) (parser *Parse return p, err } -func (p *Parser) Eval(params map[string]interface{}) (interface{}, error) { +func (p *Parser) Eval(params interface{}) (interface{}, error) { return doStage(p.stageV, params) } @@ -354,7 +354,7 @@ func mirrorStageSubtree(stages []*stage) { } } -func doStage(stage *stage, parameters map[string]interface{}) (interface{}, error) { +func doStage(stage *stage, parameters interface{}) (interface{}, error) { if stage == nil { return nil, nil diff --git a/kit/rule/parser_test.go b/kit/rule/parser_test.go index ab3cc1c..8bb5978 100644 --- a/kit/rule/parser_test.go +++ b/kit/rule/parser_test.go @@ -2,11 +2,7 @@ package rule import ( "context" - goscanner "go/scanner" - "go/token" - "strings" "testing" - "text/scanner" ) func Test_New(t *testing.T) { @@ -19,22 +15,27 @@ func Test_New(t *testing.T) { Hobby []T `json:"hobby"` } //expr := `(foo - 90 > 0 ) && ( foo > 1 || foo <1 ) && foo > 1` - expr := `foo.bar.Hobby.0.Name` - param := map[string]interface{}{ - "foo": map[string]interface{}{ - "bar": T{Name: "tom", Hobby: []T{{Name: "jay"}}}, - }, - "in": 12.2, - } - parser, err := New(context.Background(), expr, WithCustomFn("test", func(arguments ...interface{}) (interface{}, error) { + //expr := `foo.bar.Hobby.0.Name == "jay" && func test(foo.bar.Hobby.0,1,2,3)` + //param := map[string]interface{}{ + // "foo": map[string]interface{}{ + // "bar": T{Name: "tom", Hobby: []T{{Name: "jay"}}}, + // }, + // "in": 12.2, + //} + tt := T{ + Name: "ttt", + Hobby: nil, + } + parser, err := New(context.Background(), "Name", WithCustomFn("test", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { logger.Println("i am test func") + logger.Println("evalParam: ", evalParam) logger.Println(arguments...) return true, nil })) if err != nil { panic(err) } - res, err := parser.Eval(param) + res, err := parser.Eval(tt) if err != nil { panic(err) } @@ -55,44 +56,3 @@ func Test_Do(t *testing.T) { } logger.Printf("res=%v\t type=%T\t err=%+v \n", res, res, err) } - -func Test_B(t *testing.T) { - // src is the input that we want to tokenize. - src := []byte(`;a >= 6 && a != "abc" && a.ABC() != 0;; // Euler.`) - //src := []byte(`func A() int64 { return 1 } // Euler.`) - - // Initialize the scanner. - var s goscanner.Scanner - fset := token.NewFileSet() // positions are relative to fset - file := fset.AddFile("", fset.Base(), len(src)) // register input "file" - s.Init(file, src, nil /* no error handler */, goscanner.ScanComments) - - // Repeated calls to Scan yield the token sequence found in the input. - for { - pos, tok, lit := s.Scan() - if tok == token.EOF { - break - } - if tok == token.SEMICOLON { - logger.Println("got SEMICOLON") - } - logger.Printf("pos:%s\t token:%s\t lit:%q\n", fset.Position(pos), tok, lit) - } -} - -func Test_A(t *testing.T) { - exp := "a >= 6" - - var s scanner.Scanner - s.Init(strings.NewReader(exp)) - - s.Filename = exp + "\t" - s.Error = func(s *scanner.Scanner, msg string) { - logger.Println("Scanner err msg: ", msg) - } - - for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { - sT := s.TokenText() - logger.Println("token text: ", sT) - } -} diff --git a/kit/rule/rule_func_test.go b/kit/rule/rule_func_test.go index 47d79b5..73f2275 100644 --- a/kit/rule/rule_func_test.go +++ b/kit/rule/rule_func_test.go @@ -28,7 +28,7 @@ func Test_Func(t *testing.T) { { name: "Single function", expr: "func foo()", - extension: Function("foo", func(arguments ...interface{}) (interface{}, error) { + extension: Function("foo", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return true, nil }), @@ -37,7 +37,7 @@ func Test_Func(t *testing.T) { { name: "Func with argument", expr: "func passthrough(1)", - extension: Function("passthrough", func(arguments ...interface{}) (interface{}, error) { + extension: Function("passthrough", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return arguments[0], nil }), want: 1.0, @@ -45,7 +45,7 @@ func Test_Func(t *testing.T) { { name: "Func with arguments", expr: "func passthrough(1, 2)", - extension: Function("passthrough", func(arguments ...interface{}) (interface{}, error) { + extension: Function("passthrough", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return arguments[0].(float64) + arguments[1].(float64), nil }), want: 3.0, @@ -53,7 +53,7 @@ func Test_Func(t *testing.T) { { name: "Nested function with operatorPrecedence", expr: "func sum(1, func sum(2, 3), 2 + 2, 2 * 2)", - extension: Function("sum", func(arguments ...interface{}) (interface{}, error) { + extension: Function("sum", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { sum := 0.0 for _, v := range arguments { sum += v.(float64) @@ -65,7 +65,7 @@ func Test_Func(t *testing.T) { { name: "Empty function and modifier, compared", expr: "func numeric()-1 > 0", - extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) { + extension: Function("numeric", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return 2.0, nil }), want: true, @@ -73,7 +73,7 @@ func Test_Func(t *testing.T) { { name: "Empty function comparator", expr: "func numeric() > 0", - extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) { + extension: Function("numeric", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return 2.0, nil }), want: true, @@ -82,7 +82,7 @@ func Test_Func(t *testing.T) { name: "Empty function logical operator", expr: "func success() && !false", - extension: Function("success", func(arguments ...interface{}) (interface{}, error) { + extension: Function("success", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return true, nil }), want: true, @@ -90,7 +90,7 @@ func Test_Func(t *testing.T) { { name: "Empty function with prefix", expr: "-func ten()", - extension: Function("ten", func(arguments ...interface{}) (interface{}, error) { + extension: Function("ten", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return 10.0, nil }), want: -10.0, @@ -98,7 +98,7 @@ func Test_Func(t *testing.T) { { name: "Empty function as part of chain", expr: "10 - func numeric() - 2", - extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) { + extension: Function("numeric", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return 5.0, nil }), want: 3.0, @@ -106,7 +106,7 @@ func Test_Func(t *testing.T) { { name: "Enclosed empty function with modifier and comparator (#28)", expr: "(func ten() - 1) > 3", - extension: Function("ten", func(arguments ...interface{}) (interface{}, error) { + extension: Function("ten", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { return 10.0, nil }), want: true, @@ -114,7 +114,7 @@ func Test_Func(t *testing.T) { { name: "Variadic", expr: `func sum(1,2,3,4)`, - extension: Function("sum", func(arguments ...interface{}) (interface{}, error) { + extension: Function("sum", func(evalParam interface{}, arguments ...interface{}) (interface{}, error) { sum := 0. for _, a := range arguments { sum += a.(float64) diff --git a/kit/rule/rule_params_test.go b/kit/rule/rule_params_test.go index 3b7a077..d474f1a 100644 --- a/kit/rule/rule_params_test.go +++ b/kit/rule/rule_params_test.go @@ -40,7 +40,7 @@ func Test_Params(t *testing.T) { name string expr string want interface{} - params map[string]interface{} + params interface{} }{ { name: "Single parameter modified by constant", @@ -115,6 +115,57 @@ func Test_Params(t *testing.T) { expr: "(2 + 2) >= 4", want: true, }, + { + + name: "String concat with single string parameter", + expr: `foo + "bar"`, + params: map[string]interface{}{ + "foo": "baz"}, + want: "bazbar", + }, + { + + name: "String concat with multiple string parameter", + expr: "foo + bar", + params: map[string]interface{}{ + "foo": "baz", + "bar": "quux", + }, + want: "bazquux", + }, + { + + name: "String concat with float parameter", + expr: "foo + bar", + params: map[string]interface{}{ + "foo": "baz", + "bar": 123.0, + }, + want: "baz123", + }, + { + + name: "Mixed multiple string concat", + expr: `foo + 123 + "bar" + "true"`, + params: map[string]interface{}{"foo": "baz"}, + want: "baz123bartrue", + }, + { + + name: "Integer width spectrum", + expr: "uint8 + uint16 + uint32 + uint64 + int8 + int16 + int32 + int64", + params: map[string]interface{}{ + "uint8": uint8(0), + "uint16": uint16(0), + "uint32": uint32(0), + "uint64": uint64(0), + "int8": int8(0), + "int16": int16(0), + "int32": int32(0), + "int64": int64(0), + }, + want: 0.0, + }, { name: "Two-boolean logical operation (for issue #8)", diff --git a/kit/rule/token_base.go b/kit/rule/token_base.go index 6dfadc4..7e0f7a4 100644 --- a/kit/rule/token_base.go +++ b/kit/rule/token_base.go @@ -66,24 +66,20 @@ type comparableBase struct { } func (t *comparableBase) LeftCheckFn() ParamCheckFn { - return func(left, right interface{}, param map[string]interface{}) error { - leftT := fmt.Sprintf("%T", left) - rightT := fmt.Sprintf("%T", right) - if leftT != rightT { - return fmt.Errorf("left type=%s should be equal to right type=%s", leftT, rightT) - + return func(left, right interface{}, param interface{}) error { + _, ok := convertToFloat(left) + if !ok { + return fmt.Errorf("left should be a Number, but got %T, value=%v", left, left) } return nil } } func (t *comparableBase) RightCheckFn() ParamCheckFn { - return func(left, right interface{}, param map[string]interface{}) error { - leftT := fmt.Sprintf("%T", left) - rightT := fmt.Sprintf("%T", right) - if leftT != rightT { - return fmt.Errorf("left type=%s should be equal right type=%s", leftT, rightT) - + return func(left, right interface{}, param interface{}) error { + _, ok := convertToFloat(right) + if !ok { + return fmt.Errorf("right should be a Number, but got %T, value=%v", right, right) } return nil } @@ -110,7 +106,7 @@ type boolBase struct { } func (t *boolBase) LeftCheckFn() ParamCheckFn { - return func(left, right interface{}, param map[string]interface{}) error { + return func(left, right interface{}, param interface{}) error { switch left.(type) { case bool: return nil @@ -121,7 +117,7 @@ func (t *boolBase) LeftCheckFn() ParamCheckFn { } func (t *boolBase) RightCheckFn() ParamCheckFn { - return func(left, right interface{}, param map[string]interface{}) error { + return func(left, right interface{}, param interface{}) error { switch right.(type) { case bool: return nil diff --git a/kit/rule/token_bit.go b/kit/rule/token_bit.go index 82e0c5e..3485366 100644 --- a/kit/rule/token_bit.go +++ b/kit/rule/token_bit.go @@ -10,7 +10,7 @@ func (t *tokenAND) Symbol() Symbol { } func (t *tokenAND) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return float64(int64(left.(float64)) & int64(right.(float64))), nil } } @@ -25,7 +25,7 @@ func (t *tokenOR) Symbol() Symbol { } func (t *tokenOR) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return float64(int64(left.(float64)) | int64(right.(float64))), nil } } @@ -40,7 +40,7 @@ func (t *tokenXOR) Symbol() Symbol { } func (t *tokenXOR) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return float64(int64(left.(float64)) ^ int64(right.(float64))), nil } } @@ -55,7 +55,7 @@ func (t *tokenSHL) Symbol() Symbol { } func (t *tokenSHL) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return float64(uint64(left.(float64)) << uint64(right.(float64))), nil } } @@ -70,7 +70,7 @@ func (t *tokenSHR) Symbol() Symbol { } func (t *tokenSHR) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return float64(uint64(left.(float64)) >> uint64(right.(float64))), nil } } diff --git a/kit/rule/token_comparable.go b/kit/rule/token_comparable.go index ad71d48..4bc741f 100644 --- a/kit/rule/token_comparable.go +++ b/kit/rule/token_comparable.go @@ -1,6 +1,7 @@ package rule import ( + "fmt" "reflect" ) @@ -13,8 +14,24 @@ func (t *tokenEQL) Symbol() Symbol { return EQL } +func (t *tokenEQL) LeftCheckFn() ParamCheckFn { + return func(left, right interface{}, param interface{}) error { + leftT, rightT, ok := typeEqual(left, right) + if !ok { + return fmt.Errorf("tokenEQL left type=%s should be equal to right type=%s", leftT.String(), rightT) + } + return nil + } +} + +func (t *tokenEQL) RightCheckFn() ParamCheckFn { + return func(left, right interface{}, param interface{}) error { + return nil + } +} + func (t *tokenEQL) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return reflect.DeepEqual(left, right), nil } } @@ -28,8 +45,24 @@ func (t *tokenNEQ) Symbol() Symbol { return NEQ } +func (t *tokenNEQ) LeftCheckFn() ParamCheckFn { + return func(left, right interface{}, param interface{}) error { + leftT, rightT, ok := typeEqual(left, right) + if !ok { + return fmt.Errorf("tokenEQL left type=%s should be equal to right type=%s", leftT.String(), rightT) + } + return nil + } +} + +func (t *tokenNEQ) RightCheckFn() ParamCheckFn { + return func(left, right interface{}, param interface{}) error { + return nil + } +} + func (t *tokenNEQ) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return !reflect.DeepEqual(left, right), nil } } @@ -44,7 +77,7 @@ func (t *tokenGTR) Symbol() Symbol { } func (t *tokenGTR) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(float64) > right.(float64), nil } } @@ -59,7 +92,7 @@ func (t *tokenGEQ) Symbol() Symbol { } func (t *tokenGEQ) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(float64) >= right.(float64), nil } } @@ -74,7 +107,7 @@ func (t *tokenLSS) Symbol() Symbol { } func (t *tokenLSS) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(float64) < right.(float64), nil } } @@ -89,7 +122,7 @@ func (t *tokenLEQ) Symbol() Symbol { } func (t *tokenLEQ) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(float64) <= right.(float64), nil } } diff --git a/kit/rule/token_interface.go b/kit/rule/token_interface.go index ab4c495..fbe44d7 100644 --- a/kit/rule/token_interface.go +++ b/kit/rule/token_interface.go @@ -9,9 +9,9 @@ import ( type ( Symbol int64 - SymbolFn func(left, right interface{}, param map[string]interface{}) (interface{}, error) + SymbolFn func(left, right interface{}, param interface{}) (interface{}, error) - ParamCheckFn func(left, right interface{}, param map[string]interface{}) error + ParamCheckFn func(left, right interface{}, param interface{}) error Token interface { Pos() token.Pos @@ -31,7 +31,7 @@ type ( ) func getLiteralFn(literal interface{}) SymbolFn { - return func(left interface{}, right interface{}, parameters map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return literal, nil } } @@ -49,7 +49,7 @@ func (t *tokenIdent) Symbol() Symbol { } func (t *tokenIdent) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { var ( selection interface{} = param keys = strings.Split(t.lit, ".") @@ -59,7 +59,7 @@ func (t *tokenIdent) SymbolFn() SymbolFn { for _, key := range keys { selection, ok = reflectSelect(key, selection) if !ok { - return nil, fmt.Errorf("IDENT param not found:%s", t.lit) + return nil, fmt.Errorf("IDENT param(%s) NOT FOUND", t.lit) } } @@ -135,7 +135,7 @@ func (t *tokenFunc) parseParams2Arr(arr []interface{}, v interface{}) []interfac } func (t *tokenFunc) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { fn, ok := t.value.(CustomFn) if !ok || fn == nil { @@ -143,7 +143,7 @@ func (t *tokenFunc) SymbolFn() SymbolFn { } if right == nil { - return fn() + return fn(param) } logger.Printf("func right %v, %T \n", right, right) @@ -151,7 +151,7 @@ func (t *tokenFunc) SymbolFn() SymbolFn { params := make([]interface{}, 0) params = t.parseParams2Arr(params, right) - return fn(params...) + return fn(param, params...) } } @@ -175,7 +175,7 @@ func (t *tokenSeparator) Symbol() Symbol { } func (t *tokenSeparator) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { var ret []interface{} switch left := left.(type) { @@ -270,6 +270,8 @@ func (t *tokenString) CanNext(token Token) error { NEQ, // != RPAREN, // ) ADD, // + + LAND, // && + LOR, // || } return t.canRunNext(validNextKinds, token) @@ -318,7 +320,7 @@ func (t *tokenNull) Symbol() Symbol { } func (t *tokenNull) SymbolFn() SymbolFn { - return func(left interface{}, right interface{}, parameters map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return right, nil } } diff --git a/kit/rule/token_logic.go b/kit/rule/token_logic.go index 1e53130..78a2eb1 100644 --- a/kit/rule/token_logic.go +++ b/kit/rule/token_logic.go @@ -10,7 +10,7 @@ func (t *tokenLOR) Symbol() Symbol { } func (t *tokenLOR) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(bool) || right.(bool), nil } } @@ -25,7 +25,7 @@ func (t *tokenLAND) Symbol() Symbol { } func (t *tokenLAND) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(bool) && right.(bool), nil } } diff --git a/kit/rule/token_prefix.go b/kit/rule/token_prefix.go index 3eeaabd..63603d5 100644 --- a/kit/rule/token_prefix.go +++ b/kit/rule/token_prefix.go @@ -8,7 +8,7 @@ type tokenNOT struct { } func (t *tokenNOT) RightCheckFn() ParamCheckFn { - return func(left, right interface{}, param map[string]interface{}) error { + return func(left, right interface{}, param interface{}) error { switch right.(type) { case bool: return nil @@ -23,7 +23,7 @@ func (t *tokenNOT) Symbol() Symbol { } func (t *tokenNOT) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return !right.(bool), nil } } @@ -49,7 +49,7 @@ func (t *tokenNEGATE) Symbol() Symbol { } func (t *tokenNEGATE) RightCheckFn() ParamCheckFn { - return func(left, right interface{}, param map[string]interface{}) error { + return func(left, right interface{}, param interface{}) error { _, ok1 := right.(int) _, ok2 := right.(float64) if !ok1 && !ok2 { @@ -61,7 +61,7 @@ func (t *tokenNEGATE) RightCheckFn() ParamCheckFn { } func (t *tokenNEGATE) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return -right.(float64), nil } } diff --git a/kit/rule/tooken_compute.go b/kit/rule/tooken_compute.go index 1666e69..1bf3167 100644 --- a/kit/rule/tooken_compute.go +++ b/kit/rule/tooken_compute.go @@ -1,6 +1,9 @@ package rule -import "fmt" +import ( + "fmt" + "math" +) // + type tokenADD struct { @@ -11,21 +14,33 @@ func (t *tokenADD) Symbol() Symbol { return ADD } -func (t *tokenADD) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { - l1, ok1 := left.(float64) - r1, ok2 := right.(float64) - if ok1 && ok2 { - return l1 + r1, nil +func (t *tokenADD) LeftCheckFn() ParamCheckFn { + return func(left, right interface{}, param interface{}) error { + _, ok := convertToFloat(left) + if !isString(left) && !ok { + return fmt.Errorf("add left should be a Number or String, but got %T, value=%v", left, left) + } + return nil + } +} + +func (t *tokenADD) RightCheckFn() ParamCheckFn { + return func(left, right interface{}, param interface{}) error { + _, ok := convertToFloat(right) + if !isString(right) && !ok { + return fmt.Errorf("add right should be a Number or String, but got %T, value=%v", right, right) } + return nil + } +} - l2, ok1 := left.(string) - r2, ok2 := right.(string) - if ok1 && ok2 { - return l2 + r2, nil +func (t *tokenADD) SymbolFn() SymbolFn { + return func(left, right interface{}, param interface{}) (interface{}, error) { + if isString(left) || isString(right) { + return fmt.Sprintf("%v%v", left, right), nil } - return nil, fmt.Errorf("tokenADD unsupported type to do add, left=%v,right=%v", left, right) + return left.(float64) + right.(float64), nil } } @@ -39,7 +54,7 @@ func (t *tokenSUB) Symbol() Symbol { } func (t *tokenSUB) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(float64) - right.(float64), nil } } @@ -54,7 +69,7 @@ func (t *tokenMUL) Symbol() Symbol { } func (t *tokenMUL) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(float64) * right.(float64), nil } } @@ -69,7 +84,7 @@ func (t *tokenQUO) Symbol() Symbol { } func (t *tokenQUO) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { + return func(left, right interface{}, param interface{}) (interface{}, error) { return left.(float64) / right.(float64), nil } } @@ -84,7 +99,7 @@ func (t *tokenREM) Symbol() Symbol { } func (t *tokenREM) SymbolFn() SymbolFn { - return func(left, right interface{}, param map[string]interface{}) (interface{}, error) { - return float64(uint64(left.(float64)) % uint64(right.(float64))), nil + return func(left, right interface{}, param interface{}) (interface{}, error) { + return math.Mod(left.(float64), right.(float64)), nil } }