-
Notifications
You must be signed in to change notification settings - Fork 207
/
evaluable.go
171 lines (141 loc) · 5.59 KB
/
evaluable.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
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// ================================================================
// This handles anything on the right-hand sides of assignment statements.
// (Also, computed field names on the left-hand sides of assignment
// statements.)
// ================================================================
package cst
import (
"fmt"
"os"
"github.com/johnkerl/miller/pkg/dsl"
"github.com/johnkerl/miller/pkg/lib"
"github.com/johnkerl/miller/pkg/mlrval"
"github.com/johnkerl/miller/pkg/runtime"
)
// ----------------------------------------------------------------
func (root *RootNode) BuildEvaluableNode(astNode *dsl.ASTNode) (IEvaluable, error) {
if astNode.Children == nil {
return root.BuildLeafNode(astNode)
}
switch astNode.Type {
case dsl.NodeTypeArrayLiteral: // [...]
return root.BuildArrayLiteralNode(astNode)
case dsl.NodeTypeMapLiteral: // {...}
return root.BuildMapLiteralNode(astNode)
case dsl.NodeTypeArrayOrMapIndexAccess: // x[...]
return root.BuildArrayOrMapIndexAccessNode(astNode)
case dsl.NodeTypeArraySliceAccess: // myarray[lo:hi]
return root.BuildArraySliceAccessNode(astNode)
case dsl.NodeTypePositionalFieldName: // $[[...]]
return root.BuildPositionalFieldNameNode(astNode)
case dsl.NodeTypePositionalFieldValue: // $[[[...]]]
return root.BuildPositionalFieldValueNode(astNode)
case dsl.NodeTypeArrayOrMapPositionalNameAccess: // mymap[[...]]]
return root.BuildArrayOrMapPositionalNameAccessNode(astNode)
case dsl.NodeTypeArrayOrMapPositionalValueAccess: // mymap[[[...]]]
return root.BuildArrayOrMapPositionalValueAccessNode(astNode)
case dsl.NodeTypeIndirectFieldValue: // $[...]
return root.BuildIndirectFieldValueNode(astNode)
case dsl.NodeTypeIndirectOosvarValue: // $[...]
return root.BuildIndirectOosvarValueNode(astNode)
case dsl.NodeTypeEnvironmentVariable: // ENV["NAME"]
return root.BuildEnvironmentVariableNode(astNode)
// Operators are just functions with infix syntax so we treat them like
// functions in the CST. (The distinction between infix syntax, e.g.
// '1+2', and prefix syntax, e.g. 'plus(1,2)' disappears post-parse -- both
// parse to the same-shape AST.)
case dsl.NodeTypeOperator:
return root.BuildFunctionCallsiteNode(astNode)
case dsl.NodeTypeFunctionCallsite:
return root.BuildFunctionCallsiteNode(astNode)
// The dot operator is a little different from other operators since it's
// type-dependent: for strings/int/bools etc it's just concatenation of
// string representations, but if the left-hand side is a map, it's a
// key-lookup with an unquoted literal on the right. E.g. mymap.foo is the
// same as mymap["foo"].
case dsl.NodeTypeDotOperator:
return root.BuildDotCallsiteNode(astNode)
// Function literals like 'func (a,b) { return b - a }'
case dsl.NodeTypeUnnamedFunctionDefinition:
return root.BuildUnnamedUDFNode(astNode)
}
return nil, fmt.Errorf(
"at CST BuildEvaluableNode: unhandled AST node type %s", string(astNode.Type),
)
}
// ----------------------------------------------------------------
type IndirectFieldValueNode struct {
fieldNameEvaluable IEvaluable
}
func (root *RootNode) BuildIndirectFieldValueNode(
astNode *dsl.ASTNode,
) (*IndirectFieldValueNode, error) {
lib.InternalCodingErrorIf(astNode.Type != dsl.NodeTypeIndirectFieldValue)
lib.InternalCodingErrorIf(astNode.Children == nil)
lib.InternalCodingErrorIf(len(astNode.Children) != 1)
fieldNameEvaluable, err := root.BuildEvaluableNode(astNode.Children[0])
if err != nil {
return nil, err
}
return &IndirectFieldValueNode{
fieldNameEvaluable: fieldNameEvaluable,
}, nil
}
func (node *IndirectFieldValueNode) Evaluate(
state *runtime.State,
) *mlrval.Mlrval { // TODO: err
fieldName := node.fieldNameEvaluable.Evaluate(state)
if fieldName.IsAbsent() {
return mlrval.ABSENT.StrictModeCheck(state.StrictMode, "$[(absent)]")
}
// For normal DSL use the CST validator will prohibit this from being
// called in places the current record is undefined (begin and end blocks).
// However in the REPL people can read past end of stream and still try to
// print inrec attributes. Also, a UDF/UDS invoked from begin/end could try
// to access the inrec, and that would get past the validator.
if state.Inrec == nil {
return mlrval.ABSENT.StrictModeCheck(state.StrictMode, "$*")
}
value, err := state.Inrec.GetWithMlrvalIndex(fieldName)
if err != nil {
// Key isn't int or string.
// TODO: needs error-return in the API
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if value == nil {
return mlrval.ABSENT.StrictModeCheck(state.StrictMode, "$["+fieldName.String()+"]")
}
return value
}
// ----------------------------------------------------------------
type IndirectOosvarValueNode struct {
oosvarNameEvaluable IEvaluable
}
func (root *RootNode) BuildIndirectOosvarValueNode(
astNode *dsl.ASTNode,
) (*IndirectOosvarValueNode, error) {
lib.InternalCodingErrorIf(astNode.Type != dsl.NodeTypeIndirectOosvarValue)
lib.InternalCodingErrorIf(astNode.Children == nil)
lib.InternalCodingErrorIf(len(astNode.Children) != 1)
oosvarNameEvaluable, err := root.BuildEvaluableNode(astNode.Children[0])
if err != nil {
return nil, err
}
return &IndirectOosvarValueNode{
oosvarNameEvaluable: oosvarNameEvaluable,
}, nil
}
func (node *IndirectOosvarValueNode) Evaluate(
state *runtime.State,
) *mlrval.Mlrval { // TODO: err
oosvarName := node.oosvarNameEvaluable.Evaluate(state)
if oosvarName.IsAbsent() {
return mlrval.ABSENT.StrictModeCheck(state.StrictMode, "@[(absent)]")
}
value := state.Oosvars.Get(oosvarName.String())
if value == nil {
return mlrval.ABSENT.StrictModeCheck(state.StrictMode, "@["+oosvarName.String()+"]")
}
return value
}