-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpredicate.go
More file actions
110 lines (98 loc) · 2.55 KB
/
Copy pathpredicate.go
File metadata and controls
110 lines (98 loc) · 2.55 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
package jpath
import "fmt"
type predicateFunc func(*FilterCtx) bool
func selectPredicate(predicate predicateFunc) SelectorFunc {
return func(out []any, node, root any) []any {
return appendFilter(out, node, root, predicate)
}
}
// a filter that never reads the current node reaches the same verdict for every
// child, so it runs once per node and takes all children or none
func selectConstantPredicate(predicate predicateFunc) SelectorFunc {
return func(out []any, node, root any) []any {
ctx := &FilterCtx{Root: root}
if !predicate(ctx) {
return out
}
return appendWildcard(out, node)
}
}
func compilePredicate(expr FilterExpr, reg *Registry) (predicateFunc, error) {
switch v := expr.(type) {
case *PathValueExpr:
if isSingularPath(v.Path) {
lookup := makeContextLookup(v)
return func(ctx *FilterCtx) bool {
_, ok := lookup(ctx)
return ok
}, nil
}
case *UnaryExpr:
if v.Op != OpNot {
return nil, fmt.Errorf("%w: unary %s", ErrUnknownOperator, v.Op)
}
predicate, err := compilePredicate(v.Expr, reg)
if err != nil {
return nil, err
}
return func(ctx *FilterCtx) bool {
return !predicate(ctx)
}, nil
case *BinaryExpr:
switch v.Op {
case OpAnd, OpOr:
return compileLogical(v, reg)
case OpEq, OpNe, OpLt, OpLte, OpGt, OpGte:
return compileComparison(v, reg)
default:
return nil, fmt.Errorf("%w: %s", ErrUnknownOperator, v.Op)
}
}
filter, err := compileFilter(expr, reg)
if err != nil {
return nil, err
}
return func(ctx *FilterCtx) bool {
return toBool(filter(ctx))
}, nil
}
func makeContextLookup(expr *PathValueExpr) func(*FilterCtx) (any, bool) {
lookup := makeSingularLookup(expr.Path.Segments)
absolute := expr.Absolute
return func(ctx *FilterCtx) (any, bool) {
node := ctx.Current
if absolute {
node = ctx.Root
}
return lookup(node)
}
}
func compileComparison(expr *BinaryExpr, reg *Registry) (predicateFunc, error) {
op := expr.Op
left, err := compileOperand(expr.Left, reg)
if err != nil {
return nil, err
}
right, err := compileOperand(expr.Right, reg)
if err != nil {
return nil, err
}
return func(ctx *FilterCtx) bool {
return compareValues(op, comparison{left: left(ctx), right: right(ctx)})
}, nil
}
func compileOperand(expr FilterExpr, reg *Registry) (FilterFunc, error) {
switch v := expr.(type) {
case *PathValueExpr:
if isSingularPath(v.Path) {
lookup := makeContextLookup(v)
return func(ctx *FilterCtx) any {
if value, ok := lookup(ctx); ok {
return value
}
return Nodes(nil)
}, nil
}
}
return compileFilter(expr, reg)
}