-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathexpr.go
More file actions
117 lines (102 loc) · 2.69 KB
/
Copy pathexpr.go
File metadata and controls
117 lines (102 loc) · 2.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package expr
import (
"errors"
. "fmt"
"github.com/lqiz/expr/node"
"go/ast"
"go/parser"
)
// int string
// > < >= <= && ||
// in_arr(1, []int{1,2,3,4}), ver_compare(x, ">", "10.1.1") with no nested
type LogicEngine struct {
ruleAst ast.Expr
}
func NewEngine(expr string) (*LogicEngine, error) {
engine := &LogicEngine{
}
result, err := engine.UpdateAst(expr)
if err != nil || !result {
return nil, err
}
return engine, nil
}
func (engine *LogicEngine) UpdateAst(expr string) (bool, error) {
if engine == nil {
panic("please init the engine first")
}
exprAst, err := parser.ParseExpr(expr)
if err != nil {
Println(err)
return false, err
}
engine.ruleAst = exprAst
return true, nil
}
func (engine *LogicEngine) RunRule(controlMap map[string]interface{}) (bool, error) {
if engine == nil || engine.ruleAst == nil {
return false, errors.New("rule expr is empty, please init it")
}
nodeMap := parseControlMap(controlMap)
value := eval(nodeMap, engine.ruleAst)
bValue, ok := value.(node.BoolNode)
if !ok {
return false, errors.New(value.GetTextValue())
}
return bValue.True, nil
}
func parseControlMap(controlMap map[string]interface{}) map[string]node.ValueNode {
nodeMap := make(map[string]node.ValueNode, len(controlMap))
for key, control := range controlMap {
switch control.(type) {
case int:
node := node.NewIntNode(int64(control.(int)))
nodeMap[key] = node
case int64:
node := node.NewIntNode(control.(int64))
nodeMap[key] = node
case float64:
// value from json will be always float64
node := node.NewIntNode(int64(control.(float64)))
nodeMap[key] = node
case string:
node := node.NewStrNode(control.(string))
nodeMap[key] = node
}
}
return nodeMap
}
func eval(mem map[string]node.ValueNode, expr ast.Expr) (y node.ValueNode) {
switch x := expr.(type) {
case *ast.BasicLit:
return node.Lit2ValueNode(x)
case *ast.BinaryExpr:
a := eval(mem, x.X)
b := eval(mem, x.Y)
op := x.Op
if a == nil || b == nil {
return node.NewBadNode(Sprintf("%+v, %+v is nil", a, b))
}
switch a.GetType() {
case node.TypeInt64:
return BinaryIntExpr{}.Invoke(a, b, op)
case node.TypeBool:
return BinaryBoolExpr{}.Invoke(a, b, op)
case node.TypeStr:
return BinaryStrExpr{}.Invoke(a, b, op)
case node.TypeBad:
return node.NewBadNode("a:" + a.GetTextValue() + "b:" + b.GetTextValue())
}
return node.NewBadNode(Sprintf("%d op is not suppoort", op))
case *ast.CallExpr:
name := x.Fun.(*ast.Ident).Name
return CallExpr{name, x.Args}.Invoke(mem)
case *ast.ParenExpr:
return eval(mem, x.X)
case *ast.Ident:
return mem[x.Name]
default:
return node.NewBadNode(Sprintf("%x type is not suppoort", x))
}
panic("internal error")
}