forked from expr-lang/expr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpredicate_combination.go
61 lines (56 loc) · 1.68 KB
/
predicate_combination.go
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
package optimizer
import (
. "github.com/expr-lang/expr/ast"
"github.com/expr-lang/expr/parser/operator"
)
/*
predicateCombination is a visitor that combines multiple predicate calls into a single call.
For example, the following expression:
all(x, x > 1) && all(x, x < 10) -> all(x, x > 1 && x < 10)
any(x, x > 1) || any(x, x < 10) -> any(x, x > 1 || x < 10)
none(x, x > 1) && none(x, x < 10) -> none(x, x > 1 || x < 10)
*/
type predicateCombination struct{}
func (v *predicateCombination) Visit(node *Node) {
if op, ok := (*node).(*BinaryNode); ok && operator.IsBoolean(op.Operator) {
if left, ok := op.Left.(*BuiltinNode); ok {
if combinedOp, ok := combinedOperator(left.Name, op.Operator); ok {
if right, ok := op.Right.(*BuiltinNode); ok && right.Name == left.Name {
if left.Arguments[0].Type() == right.Arguments[0].Type() && left.Arguments[0].String() == right.Arguments[0].String() {
predicate := &PredicateNode{
Node: &BinaryNode{
Operator: combinedOp,
Left: left.Arguments[1].(*PredicateNode).Node,
Right: right.Arguments[1].(*PredicateNode).Node,
},
}
v.Visit(&predicate.Node)
patchCopyType(node, &BuiltinNode{
Name: left.Name,
Arguments: []Node{
left.Arguments[0],
predicate,
},
})
}
}
}
}
}
}
func combinedOperator(fn, op string) (string, bool) {
switch {
case fn == "all" && (op == "and" || op == "&&"):
return op, true
case fn == "any" && (op == "or" || op == "||"):
return op, true
case fn == "none" && (op == "and" || op == "&&"):
switch op {
case "and":
return "or", true
case "&&":
return "||", true
}
}
return "", false
}