forked from influxdata/kapacitor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
node_evaluator.go
141 lines (115 loc) · 4.05 KB
/
node_evaluator.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
package stateful
import (
"errors"
"fmt"
"regexp"
"time"
"github.com/influxdata/kapacitor/tick/ast"
)
// ErrTypeGuardFailed is returned when a speicifc value type is requested thorugh NodeEvaluator (for example: "Float64Value")
// when the node doesn't support the given type, for example "Float64Value" is called on BoolNode
type ErrTypeGuardFailed struct {
RequestedType ast.ValueType
ActualType ast.ValueType
}
func (e ErrTypeGuardFailed) Error() string {
return fmt.Sprintf("TypeGuard: expression returned unexpected type %s, expected %s", e.ActualType, e.RequestedType)
}
type ReadOnlyScope interface {
Get(name string) (interface{}, error)
References() []string
DynamicFunc(name string) *DynamicFunc
}
// NodeEvaluator provides a generic way for trying to fetch
// node value, if a speicifc type is requested (so Value isn't called, the *Value is called) ErrTypeGuardFailed must be returned
type NodeEvaluator interface {
EvalFloat(scope *Scope, executionState ExecutionState) (float64, error)
EvalInt(scope *Scope, executionState ExecutionState) (int64, error)
EvalString(scope *Scope, executionState ExecutionState) (string, error)
EvalBool(scope *Scope, executionState ExecutionState) (bool, error)
EvalRegex(scope *Scope, executionState ExecutionState) (*regexp.Regexp, error)
EvalTime(scope *Scope, executionState ExecutionState) (time.Time, error)
EvalDuration(scope *Scope, executionState ExecutionState) (time.Duration, error)
EvalMissing(scope *Scope, executionState ExecutionState) (*ast.Missing, error)
// Type returns the type of ast.ValueType
Type(scope ReadOnlyScope) (ast.ValueType, error)
// Whether the type returned by the node can change.
IsDynamic() bool
}
func createNodeEvaluator(n ast.Node) (NodeEvaluator, error) {
switch node := n.(type) {
case *ast.BoolNode:
return &EvalBoolNode{Node: node}, nil
case *ast.NumberNode:
switch {
case node.IsFloat:
return &EvalFloatNode{Float64: node.Float64}, nil
case node.IsInt:
return &EvalIntNode{Int64: node.Int64}, nil
default:
// We wouldn't reach ever, unless there is bug in tick parsing ;)
return nil, errors.New("Invalid NumberNode: Not float or int")
}
case *ast.DurationNode:
return &EvalDurationNode{Duration: node.Dur}, nil
case *ast.StringNode:
return &EvalStringNode{Node: node}, nil
case *ast.RegexNode:
return &EvalRegexNode{Node: node}, nil
case *ast.BinaryNode:
return NewEvalBinaryNode(node)
case *ast.ReferenceNode:
return &EvalReferenceNode{Node: node}, nil
case *ast.FunctionNode:
return NewEvalFunctionNode(node)
case *ast.UnaryNode:
return NewEvalUnaryNode(node)
case *ast.LambdaNode:
return NewEvalLambdaNode(node)
}
return nil, fmt.Errorf("Given node type is not valid evaluation node: %T", n)
}
// getCostantNodeType - Given a ast.Node we want to know it's return type
// this method does exactly this, few examples:
// *) StringNode -> ast.TString
// *) UnaryNode -> we base the type by the node type
func getConstantNodeType(n ast.Node) ast.ValueType {
switch node := n.(type) {
case *ast.NumberNode:
if node.IsInt {
return ast.TInt
}
if node.IsFloat {
return ast.TFloat
}
case *ast.DurationNode:
return ast.TDuration
case *ast.StringNode:
return ast.TString
case *ast.BoolNode:
return ast.TBool
case *ast.RegexNode:
return ast.TRegex
case *ast.UnaryNode:
// If this is comparison operator we know for sure the output must be boolean
if node.Operator == ast.TokenNot {
return ast.TBool
}
// Could be float int or duration
if node.Operator == ast.TokenMinus {
return getConstantNodeType(node.Node)
}
case *ast.BinaryNode:
// Check first using only the operator
if ast.IsCompOperator(node.Operator) || ast.IsLogicalOperator(node.Operator) {
return ast.TBool
}
leftType := getConstantNodeType(node.Left)
rightType := getConstantNodeType(node.Right)
// Check known constant types
return binaryConstantTypes[operationKey{operator: node.Operator, leftType: leftType, rightType: rightType}]
case *ast.LambdaNode:
return getConstantNodeType(node.Expression)
}
return ast.InvalidType
}