-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnode.go
More file actions
137 lines (122 loc) · 3.2 KB
/
node.go
File metadata and controls
137 lines (122 loc) · 3.2 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package structql
import (
"fmt"
"github.com/viant/igo"
"github.com/viant/igo/exec"
"github.com/viant/igo/exec/expr"
snode "github.com/viant/sqlparser/node"
"github.com/viant/structql/node"
"github.com/viant/structql/parser"
"github.com/viant/xunsafe"
"reflect"
)
type nodeKind int
const (
nodeKindUnknown = nodeKind(0)
nodeKindObject = nodeKind(2)
nodeKindArray = nodeKind(3)
)
// Node represents a node
type Node struct {
kind nodeKind
IsLeaf bool
ownerType reflect.Type
xField *xunsafe.Field
xSlice *xunsafe.Slice
selector *node.Selector
child *Node
expr *expr.Bool
exprSel *exec.Selector
}
// Type returns node Type
func (n *Node) Type() reflect.Type {
return n.ownerType
}
// Leaf returns leaf node
func (n *Node) Leaf() *Node {
if n.child != nil {
return n.child.Leaf()
}
return n
}
// LeafType returns leaf type
func (n *Node) LeafType() reflect.Type {
if n.child != nil {
return n.child.LeafType()
}
return n.ownerType
}
// When applied expr or returns true if not defined
func (n *Node) When(value interface{}) bool {
if n.expr == nil {
return true
}
state := n.expr.NewState()
n.exprSel.SetValue(state.Pointer(), value)
result := n.expr.ComputeWithState(state)
state.Release()
return result
}
// LeafOwnerType returns leaf type
func (n *Node) LeafOwnerType() reflect.Type {
if n.child != nil {
if n.child.IsLeaf {
return n.child.ownerType
}
return n.child.LeafType()
}
return n.ownerType
}
// NewNode creates a node
func NewNode(ownerType reflect.Type, sel *node.Selector, values *node.Values) (*Node, error) {
var err error
aNode := &Node{selector: sel}
aNode.ownerType = ownerType
rawType := aNode.ownerType
if aNode.ownerType.Kind() == reflect.Ptr {
rawType = aNode.ownerType.Elem()
}
aNode.IsLeaf = sel.Child == nil
switch rawType.Kind() {
case reflect.Slice:
aNode.kind = nodeKindArray
aNode.IsLeaf = false
aNode.xSlice = xunsafe.NewSlice(rawType)
if aNode.child, err = NewNode(aNode.ownerType.Elem(), sel, values); err != nil {
return nil, err
}
case reflect.Struct:
aNode.kind = nodeKindObject
if sel.Name != "" {
if aNode.xField = xunsafe.FieldByName(rawType, sel.Name); aNode.xField == nil {
return nil, fmt.Errorf("failed to lookup field: '%v' on %v", sel.Name, rawType.Name())
}
if aNode.child, err = NewNode(aNode.xField.Type, sel.Child, values); err != nil {
return nil, err
}
}
if sel.Criteria != nil {
aNode.expr, aNode.exprSel, err = compileCriteria(sel.Holder, sel.Criteria, aNode.ownerType, values)
}
default:
if aNode.IsLeaf {
return aNode, nil
}
return nil, fmt.Errorf("unsupported type:%s", ownerType.String())
}
return aNode, err
}
func compileCriteria(holder string, criteria snode.Node, ownerType reflect.Type, values *node.Values) (*expr.Bool, *exec.Selector, error) {
var err error
scope := igo.NewScope()
goExpr, err := parser.AsBinaryGoExpr(holder+".", criteria, node.LookupFieldType(holder, ownerType), values)
if err != nil {
return nil, nil, fmt.Errorf("failed to compile criteria: %w", err)
}
exprSel, err := scope.DefineVariable(holder, ownerType)
if err != nil {
return nil, nil, err
}
expr, err := scope.BoolExpression(goExpr)
return expr, exprSel, err
}