Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/river/vm/vm.go
4095 views
1
// Package vm provides a River expression evaluator.
2
package vm
3
4
import (
5
"fmt"
6
"reflect"
7
"strings"
8
9
"github.com/grafana/agent/pkg/river/ast"
10
"github.com/grafana/agent/pkg/river/diag"
11
"github.com/grafana/agent/pkg/river/internal/reflectutil"
12
"github.com/grafana/agent/pkg/river/internal/rivertags"
13
"github.com/grafana/agent/pkg/river/internal/stdlib"
14
"github.com/grafana/agent/pkg/river/internal/value"
15
)
16
17
// Evaluator evaluates River AST nodes into Go values. Each Evaluator is bound
18
// to a single AST node. To evaluate the node, call Evaluate.
19
type Evaluator struct {
20
// node for the AST.
21
//
22
// Each Evaluator is bound to a single node to allow for future performance
23
// optimizations, allowing for precomputing and storing the result of
24
// anything that is constant.
25
node ast.Node
26
}
27
28
// New creates a new Evaluator for the given AST node. The given node must be
29
// either an *ast.File, *ast.BlockStmt, ast.Body, or assignable to an ast.Expr.
30
func New(node ast.Node) *Evaluator {
31
return &Evaluator{node: node}
32
}
33
34
// Evaluate evaluates the Evaluator's node into a River value and decodes that
35
// value into the Go value v.
36
//
37
// Each call to Evaluate may provide a different scope with new values for
38
// available variables. If a variable used by the Evaluator's node isn't
39
// defined in scope or any of the parent scopes, Evaluate will return an error.
40
func (vm *Evaluator) Evaluate(scope *Scope, v interface{}) (err error) {
41
// Track a map that allows us to associate values with ast.Nodes so we can
42
// return decorated error messages.
43
assoc := make(map[value.Value]ast.Node)
44
45
defer func() {
46
if err != nil {
47
// Decorate the error on return.
48
err = makeDiagnostic(err, assoc)
49
}
50
}()
51
52
switch node := vm.node.(type) {
53
case *ast.BlockStmt, ast.Body:
54
rv := reflect.ValueOf(v)
55
if rv.Kind() != reflect.Pointer {
56
panic(fmt.Sprintf("river/vm: expected pointer, got %s", rv.Kind()))
57
}
58
return vm.evaluateBlockOrBody(scope, assoc, node, rv)
59
case *ast.File:
60
rv := reflect.ValueOf(v)
61
if rv.Kind() != reflect.Pointer {
62
panic(fmt.Sprintf("river/vm: expected pointer, got %s", rv.Kind()))
63
}
64
return vm.evaluateBlockOrBody(scope, assoc, node.Body, rv)
65
default:
66
expr, ok := node.(ast.Expr)
67
if !ok {
68
panic(fmt.Sprintf("river/vm: unexpected value type %T", node))
69
}
70
val, err := vm.evaluateExpr(scope, assoc, expr)
71
if err != nil {
72
return err
73
}
74
return value.Decode(val, v)
75
}
76
}
77
78
func (vm *Evaluator) evaluateBlockOrBody(scope *Scope, assoc map[value.Value]ast.Node, node ast.Node, rv reflect.Value) error {
79
// TODO(rfratto): the errors returned by this function are missing context to
80
// be able to print line numbers. We need to return decorated error types.
81
82
// Before decoding the block, we need to temporarily take the address of rv
83
// to handle the case of it implementing the unmarshaler interface.
84
if rv.CanAddr() {
85
rv = rv.Addr()
86
}
87
88
if ru, ok := rv.Interface().(value.Unmarshaler); ok {
89
return ru.UnmarshalRiver(func(v interface{}) error {
90
rv := reflect.ValueOf(v)
91
if rv.Kind() != reflect.Pointer {
92
panic(fmt.Sprintf("river/vm: expected pointer, got %s", rv.Kind()))
93
}
94
return vm.evaluateBlockOrBody(scope, assoc, node, rv.Elem())
95
})
96
}
97
98
// Fully deference rv and allocate pointers as necessary.
99
for rv.Kind() == reflect.Pointer {
100
if rv.IsNil() {
101
rv.Set(reflect.New(rv.Type().Elem()))
102
}
103
rv = rv.Elem()
104
}
105
106
if rv.Kind() == reflect.Interface {
107
var anyMap map[string]interface{}
108
into := reflect.MakeMap(reflect.TypeOf(anyMap))
109
if err := vm.evaluateMap(scope, assoc, node, into); err != nil {
110
return err
111
}
112
113
rv.Set(into)
114
return nil
115
} else if rv.Kind() == reflect.Map {
116
return vm.evaluateMap(scope, assoc, node, rv)
117
} else if rv.Kind() != reflect.Struct {
118
panic(fmt.Sprintf("river/vm: can only evaluate blocks into structs, got %s", rv.Kind()))
119
}
120
121
ti := getCachedTagInfo(rv.Type())
122
123
var stmts ast.Body
124
switch node := node.(type) {
125
case *ast.BlockStmt:
126
// Decode the block label first.
127
if err := vm.evaluateBlockLabel(node, ti.Tags, rv); err != nil {
128
return err
129
}
130
stmts = node.Body
131
case ast.Body:
132
stmts = node
133
default:
134
panic(fmt.Sprintf("river/vm: unrecognized node type %T", node))
135
}
136
137
sd := structDecoder{
138
VM: vm,
139
Scope: scope,
140
Assoc: assoc,
141
TagInfo: ti,
142
}
143
return sd.Decode(stmts, rv)
144
}
145
146
// evaluateMap evaluates a block or a body into a map.
147
func (vm *Evaluator) evaluateMap(scope *Scope, assoc map[value.Value]ast.Node, node ast.Node, rv reflect.Value) error {
148
var stmts ast.Body
149
150
switch node := node.(type) {
151
case *ast.BlockStmt:
152
if node.Label != "" {
153
return diag.Diagnostic{
154
Severity: diag.SeverityLevelError,
155
StartPos: node.NamePos.Position(),
156
EndPos: node.LCurlyPos.Position(),
157
Message: fmt.Sprintf("block %q requires non-empty label", strings.Join(node.Name, ".")),
158
}
159
}
160
stmts = node.Body
161
case ast.Body:
162
stmts = node
163
default:
164
panic(fmt.Sprintf("river/vm: unrecognized node type %T", node))
165
}
166
167
if rv.IsNil() {
168
rv.Set(reflect.MakeMap(rv.Type()))
169
}
170
171
for _, stmt := range stmts {
172
switch stmt := stmt.(type) {
173
case *ast.AttributeStmt:
174
val, err := vm.evaluateExpr(scope, assoc, stmt.Value)
175
if err != nil {
176
// TODO(rfratto): get error as diagnostics.
177
return err
178
}
179
180
target := reflect.New(rv.Type().Elem()).Elem()
181
if err := value.Decode(val, target.Addr().Interface()); err != nil {
182
// TODO(rfratto): get error as diagnostics.
183
return err
184
}
185
rv.SetMapIndex(reflect.ValueOf(stmt.Name.Name), target)
186
187
case *ast.BlockStmt:
188
// TODO(rfratto): potentially relax this restriction where nested blocks
189
// are permitted when decoding to a map.
190
return diag.Diagnostic{
191
Severity: diag.SeverityLevelError,
192
StartPos: ast.StartPos(stmt).Position(),
193
EndPos: ast.EndPos(stmt).Position(),
194
Message: "nested blocks not supported here",
195
}
196
197
default:
198
panic(fmt.Sprintf("river/vm: unrecognized node type %T", stmt))
199
}
200
}
201
202
return nil
203
}
204
205
func (vm *Evaluator) evaluateBlockLabel(node *ast.BlockStmt, tfs []rivertags.Field, rv reflect.Value) error {
206
var (
207
labelField rivertags.Field
208
foundField bool
209
)
210
for _, tf := range tfs {
211
if tf.Flags&rivertags.FlagLabel != 0 {
212
labelField = tf
213
foundField = true
214
break
215
}
216
}
217
218
// Check for user errors first.
219
//
220
// We return parser.Error here to restrict the position of the error to just
221
// the name. We might be able to clean this up in the future by extending
222
// ValueError to have an explicit position.
223
switch {
224
case node.Label == "" && foundField: // No user label, but struct expects one
225
return diag.Diagnostic{
226
Severity: diag.SeverityLevelError,
227
StartPos: node.NamePos.Position(),
228
EndPos: node.LCurlyPos.Position(),
229
Message: fmt.Sprintf("block %q requires non-empty label", strings.Join(node.Name, ".")),
230
}
231
case node.Label != "" && !foundField: // User label, but struct doesn't expect one
232
return diag.Diagnostic{
233
Severity: diag.SeverityLevelError,
234
StartPos: node.NamePos.Position(),
235
EndPos: node.LCurlyPos.Position(),
236
Message: fmt.Sprintf("block %q does not support specifying labels", strings.Join(node.Name, ".")),
237
}
238
}
239
240
if node.Label == "" {
241
// no-op: no labels to set.
242
return nil
243
}
244
245
var (
246
field = reflectutil.GetOrAlloc(rv, labelField)
247
fieldType = field.Type()
248
)
249
if !reflect.TypeOf(node.Label).AssignableTo(fieldType) {
250
// The Label struct field needs to be a string.
251
panic(fmt.Sprintf("river/vm: cannot assign block label to non-string type %s", fieldType))
252
}
253
field.Set(reflect.ValueOf(node.Label))
254
return nil
255
}
256
257
// prepareDecodeValue prepares v for decoding. Pointers will be fully
258
// dereferenced until finding a non-pointer value. nil pointers will be
259
// allocated.
260
func prepareDecodeValue(v reflect.Value) reflect.Value {
261
for v.Kind() == reflect.Pointer {
262
if v.IsNil() {
263
v.Set(reflect.New(v.Type().Elem()))
264
}
265
v = v.Elem()
266
}
267
return v
268
}
269
270
func (vm *Evaluator) evaluateExpr(scope *Scope, assoc map[value.Value]ast.Node, expr ast.Expr) (v value.Value, err error) {
271
defer func() {
272
if v != value.Null {
273
assoc[v] = expr
274
}
275
}()
276
277
switch expr := expr.(type) {
278
case *ast.LiteralExpr:
279
return valueFromLiteral(expr.Value, expr.Kind)
280
281
case *ast.BinaryExpr:
282
lhs, err := vm.evaluateExpr(scope, assoc, expr.Left)
283
if err != nil {
284
return value.Null, err
285
}
286
rhs, err := vm.evaluateExpr(scope, assoc, expr.Right)
287
if err != nil {
288
return value.Null, err
289
}
290
return evalBinop(lhs, expr.Kind, rhs)
291
292
case *ast.ArrayExpr:
293
vals := make([]value.Value, len(expr.Elements))
294
for i, element := range expr.Elements {
295
val, err := vm.evaluateExpr(scope, assoc, element)
296
if err != nil {
297
return value.Null, err
298
}
299
vals[i] = val
300
}
301
return value.Array(vals...), nil
302
303
case *ast.ObjectExpr:
304
fields := make(map[string]value.Value, len(expr.Fields))
305
for _, field := range expr.Fields {
306
val, err := vm.evaluateExpr(scope, assoc, field.Value)
307
if err != nil {
308
return value.Null, err
309
}
310
fields[field.Name.Name] = val
311
}
312
return value.Object(fields), nil
313
314
case *ast.IdentifierExpr:
315
val, found := scope.Lookup(expr.Ident.Name)
316
if !found {
317
return value.Null, diag.Diagnostic{
318
Severity: diag.SeverityLevelError,
319
StartPos: ast.StartPos(expr).Position(),
320
EndPos: ast.EndPos(expr).Position(),
321
Message: fmt.Sprintf("identifier %q does not exist", expr.Ident.Name),
322
}
323
}
324
return value.Encode(val), nil
325
326
case *ast.AccessExpr:
327
val, err := vm.evaluateExpr(scope, assoc, expr.Value)
328
if err != nil {
329
return value.Null, err
330
}
331
332
switch val.Type() {
333
case value.TypeObject:
334
res, ok := val.Key(expr.Name.Name)
335
if !ok {
336
return value.Null, diag.Diagnostic{
337
Severity: diag.SeverityLevelError,
338
StartPos: ast.StartPos(expr.Name).Position(),
339
EndPos: ast.EndPos(expr.Name).Position(),
340
Message: fmt.Sprintf("field %q does not exist", expr.Name.Name),
341
}
342
}
343
return res, nil
344
default:
345
return value.Null, value.Error{
346
Value: val,
347
Inner: fmt.Errorf("cannot access field %q on value of type %s", expr.Name.Name, val.Type()),
348
}
349
}
350
351
case *ast.IndexExpr:
352
val, err := vm.evaluateExpr(scope, assoc, expr.Value)
353
if err != nil {
354
return value.Null, err
355
}
356
idx, err := vm.evaluateExpr(scope, assoc, expr.Index)
357
if err != nil {
358
return value.Null, err
359
}
360
361
switch val.Type() {
362
case value.TypeArray:
363
// Arrays are indexed with a number.
364
if idx.Type() != value.TypeNumber {
365
return value.Null, value.TypeError{Value: idx, Expected: value.TypeNumber}
366
}
367
intIndex := int(idx.Int())
368
369
if intIndex < 0 || intIndex >= val.Len() {
370
return value.Null, value.Error{
371
Value: idx,
372
Inner: fmt.Errorf("index %d is out of range of array with length %d", intIndex, val.Len()),
373
}
374
}
375
return val.Index(intIndex), nil
376
377
case value.TypeObject:
378
// Objects are indexed with a string.
379
if idx.Type() != value.TypeString {
380
return value.Null, value.TypeError{Value: idx, Expected: value.TypeNumber}
381
}
382
383
field, ok := val.Key(idx.Text())
384
if !ok {
385
return value.Null, diag.Diagnostic{
386
Severity: diag.SeverityLevelError,
387
StartPos: ast.StartPos(expr.Index).Position(),
388
EndPos: ast.EndPos(expr.Index).Position(),
389
Message: fmt.Sprintf("field %q does not exist", idx.Text()),
390
}
391
}
392
return field, nil
393
394
default:
395
return value.Null, value.Error{
396
Value: val,
397
Inner: fmt.Errorf("expected object or array, got %s", val.Type()),
398
}
399
}
400
401
case *ast.ParenExpr:
402
return vm.evaluateExpr(scope, assoc, expr.Inner)
403
404
case *ast.UnaryExpr:
405
val, err := vm.evaluateExpr(scope, assoc, expr.Value)
406
if err != nil {
407
return value.Null, err
408
}
409
return evalUnaryOp(expr.Kind, val)
410
411
case *ast.CallExpr:
412
funcVal, err := vm.evaluateExpr(scope, assoc, expr.Value)
413
if err != nil {
414
return funcVal, err
415
}
416
if funcVal.Type() != value.TypeFunction {
417
return value.Null, value.TypeError{Value: funcVal, Expected: value.TypeFunction}
418
}
419
420
args := make([]value.Value, len(expr.Args))
421
for i := 0; i < len(expr.Args); i++ {
422
args[i], err = vm.evaluateExpr(scope, assoc, expr.Args[i])
423
if err != nil {
424
return value.Null, err
425
}
426
}
427
return funcVal.Call(args...)
428
429
default:
430
panic(fmt.Sprintf("river/vm: unexpected ast.Expr type %T", expr))
431
}
432
}
433
434
// A Scope exposes a set of variables available to use during evaluation.
435
type Scope struct {
436
// Parent optionally points to a parent Scope containing more variable.
437
// Variables defined in children scopes take precedence over variables of the
438
// same name found in parent scopes.
439
Parent *Scope
440
441
// Variables holds the list of available variable names that can be used when
442
// evaluating a node.
443
//
444
// Values in the Variables map should be considered immutable after passed to
445
// Evaluate; maps and slices will be copied by reference for performance
446
// optimizations.
447
Variables map[string]interface{}
448
}
449
450
// Lookup looks up a named identifier from the scope, all of the scope's
451
// parents, and the stdlib.
452
func (s *Scope) Lookup(name string) (interface{}, bool) {
453
// Traverse the scope first, then fall back to stdlib.
454
for s != nil {
455
if val, ok := s.Variables[name]; ok {
456
return val, true
457
}
458
s = s.Parent
459
}
460
if ident, ok := stdlib.Identifiers[name]; ok {
461
return ident, true
462
}
463
return nil, false
464
}
465
466