Expressions: Move GEL into core as expressions (#29072)
* comes from grafana/gel-app * remove transform plugin code * move __expr__ and -100 constants to expr pkg * set OrgID on request plugin context * use gtime for resample duration * in resample, rename "rule" to "window", use gtime for duration, parse duration before exec * remove gel entry from plugins-bundled/external.json which creates an empty array for plugins
This commit is contained in:
@@ -0,0 +1,422 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Parse nodes.
|
||||
|
||||
package parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Node is an element in the parse tree. The interface is trivial.
|
||||
// The interface contains an unexported method so that only
|
||||
// types local to this package can satisfy it.
|
||||
type Node interface {
|
||||
Type() NodeType
|
||||
String() string
|
||||
StringAST() string
|
||||
Position() Pos // byte position of start of node in full original input string
|
||||
Check(*Tree) error // performs type checking for itself and sub-nodes
|
||||
Return() ReturnType
|
||||
|
||||
// Make sure only functions in this package can create Nodes.
|
||||
unexported()
|
||||
}
|
||||
|
||||
// NodeType identifies the type of a parse tree node.
|
||||
type NodeType int
|
||||
|
||||
// Pos represents a byte position in the original input text from which
|
||||
// this template was parsed.
|
||||
type Pos int
|
||||
|
||||
// Position returns the integer Position of p
|
||||
func (p Pos) Position() Pos {
|
||||
return p
|
||||
}
|
||||
|
||||
// unexported keeps Node implementations local to the package.
|
||||
// All implementations embed Pos, so this takes care of it.
|
||||
func (Pos) unexported() {
|
||||
}
|
||||
|
||||
// Type returns itself and provides an easy default implementation
|
||||
// for embedding in a Node. Embedded in all non-trivial Nodes.
|
||||
func (t NodeType) Type() NodeType {
|
||||
return t
|
||||
}
|
||||
|
||||
const (
|
||||
// NodeFunc is a function call.
|
||||
NodeFunc NodeType = iota
|
||||
// NodeBinary is a binary operator: math, logical, compare
|
||||
NodeBinary
|
||||
// NodeUnary is unary operator: !, -
|
||||
NodeUnary
|
||||
// NodeString is string constant.
|
||||
NodeString
|
||||
// NodeNumber is a numerical constant (Scalar).
|
||||
NodeNumber
|
||||
// NodeVar is variable: $A
|
||||
NodeVar
|
||||
)
|
||||
|
||||
// String returns the string representation of the NodeType
|
||||
func (t NodeType) String() string {
|
||||
switch t {
|
||||
case NodeFunc:
|
||||
return "NodeFunc"
|
||||
case NodeBinary:
|
||||
return "NodeBinary"
|
||||
case NodeUnary:
|
||||
return "NodeUnary"
|
||||
case NodeString:
|
||||
return "NodeString"
|
||||
case NodeNumber:
|
||||
return "NodeNumber"
|
||||
default:
|
||||
return "NodeUnknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes.
|
||||
|
||||
// VarNode holds a variable reference.
|
||||
type VarNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
Name string // Without the $
|
||||
Text string // Raw
|
||||
}
|
||||
|
||||
func newVar(pos Pos, name, text string) *VarNode {
|
||||
return &VarNode{NodeType: NodeVar, Pos: pos, Name: name, Text: text}
|
||||
}
|
||||
|
||||
// Type returns the Type of the VarNode so it fulfills the Node interface.
|
||||
func (n *VarNode) Type() NodeType { return NodeVar }
|
||||
|
||||
// String returns the string representation of the VarNode so it fulfills the Node interface.
|
||||
func (n *VarNode) String() string { return n.Text }
|
||||
|
||||
// StringAST returns the string representation of abstract syntax tree of the VarNode so it fulfills the Node interface.
|
||||
func (n *VarNode) StringAST() string { return n.String() }
|
||||
|
||||
// Check performs parse time checking on the VarNode so it fulfills the Node interface.
|
||||
func (n *VarNode) Check(*Tree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return returns the result type of the VarNode so it fulfills the Node interface.
|
||||
func (n *VarNode) Return() ReturnType {
|
||||
return TypeSeriesSet // Vars are only time series for now I guess....
|
||||
}
|
||||
|
||||
// FuncNode holds a function invocation.
|
||||
type FuncNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
Name string
|
||||
F *Func
|
||||
Args []Node
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func newFunc(pos Pos, name string, f Func) *FuncNode {
|
||||
return &FuncNode{NodeType: NodeFunc, Pos: pos, Name: name, F: &f}
|
||||
}
|
||||
|
||||
func (f *FuncNode) append(arg Node) {
|
||||
f.Args = append(f.Args, arg)
|
||||
}
|
||||
|
||||
// String returns the string representation of the FuncNode so it fulfills the Node interface.
|
||||
func (f *FuncNode) String() string {
|
||||
s := f.Name + "("
|
||||
for i, arg := range f.Args {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += arg.String()
|
||||
}
|
||||
s += ")"
|
||||
return s
|
||||
}
|
||||
|
||||
// StringAST returns the string representation of abstract syntax tree of the FuncNode so it fulfills the Node interface.
|
||||
func (f *FuncNode) StringAST() string {
|
||||
s := f.Name + "("
|
||||
for i, arg := range f.Args {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += arg.StringAST()
|
||||
}
|
||||
s += ")"
|
||||
return s
|
||||
}
|
||||
|
||||
// Check performs parse time checking on the FuncNode so it fulfills the Node interface.
|
||||
func (f *FuncNode) Check(t *Tree) error {
|
||||
if len(f.Args) < len(f.F.Args) {
|
||||
return fmt.Errorf("parse: not enough arguments for %s", f.Name)
|
||||
} else if len(f.Args) > len(f.F.Args) {
|
||||
return fmt.Errorf("parse: too many arguments for %s", f.Name)
|
||||
}
|
||||
|
||||
for i, arg := range f.Args {
|
||||
funcType := f.F.Args[i]
|
||||
argType := arg.Return()
|
||||
// if funcType == TypeNumberSet && argType == TypeScalar {
|
||||
// argType = TypeNumberSet
|
||||
// }
|
||||
if funcType == TypeVariantSet {
|
||||
if !(argType == TypeNumberSet || argType == TypeSeriesSet || argType == TypeScalar) {
|
||||
return fmt.Errorf("parse: expected %v or %v for argument %v, got %v", TypeNumberSet, TypeSeriesSet, i, argType)
|
||||
}
|
||||
} else if funcType != argType {
|
||||
return fmt.Errorf("parse: expected %v, got %v for argument %v (%v)", funcType, argType, i, arg.String())
|
||||
}
|
||||
if err := arg.Check(t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if f.F.Check != nil {
|
||||
return f.F.Check(t, f)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return returns the result type of the FuncNode so it fulfills the Node interface.
|
||||
func (f *FuncNode) Return() ReturnType {
|
||||
return f.F.Return
|
||||
}
|
||||
|
||||
// ScalarNode holds a number: signed or unsigned integer or float.
|
||||
// The value is parsed and stored under all the types that can represent the value.
|
||||
// This simulates in a small amount of code the behavior of Go's ideal constants.
|
||||
type ScalarNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
IsUint bool // Number has an unsigned integral value.
|
||||
IsFloat bool // Number has a floating-point value.
|
||||
Uint64 uint64 // The unsigned integer value.
|
||||
Float64 float64 // The floating-point value.
|
||||
Text string // The original textual representation from the input.
|
||||
}
|
||||
|
||||
func newNumber(pos Pos, text string) (*ScalarNode, error) {
|
||||
n := &ScalarNode{NodeType: NodeNumber, Pos: pos, Text: text}
|
||||
// Do integer test first so we get 0x123 etc.
|
||||
u, err := strconv.ParseUint(text, 0, 64) // will fail for -0.
|
||||
if err == nil {
|
||||
n.IsUint = true
|
||||
n.Uint64 = u
|
||||
}
|
||||
// If an integer extraction succeeded, promote the float.
|
||||
if n.IsUint {
|
||||
n.IsFloat = true
|
||||
n.Float64 = float64(n.Uint64)
|
||||
} else {
|
||||
f, err := strconv.ParseFloat(text, 64)
|
||||
if err == nil {
|
||||
n.IsFloat = true
|
||||
n.Float64 = f
|
||||
// If a floating-point extraction succeeded, extract the int if needed.
|
||||
if !n.IsUint && float64(uint64(f)) == f {
|
||||
n.IsUint = true
|
||||
n.Uint64 = uint64(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !n.IsUint && !n.IsFloat {
|
||||
return nil, fmt.Errorf("illegal number syntax: %q", text)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// String returns the string representation of the ScalarNode so it fulfills the Node interface.
|
||||
func (n *ScalarNode) String() string {
|
||||
return n.Text
|
||||
}
|
||||
|
||||
// StringAST returns the string representation of abstract syntax tree of the ScalarNode so it fulfills the Node interface.
|
||||
func (n *ScalarNode) StringAST() string {
|
||||
return n.String()
|
||||
}
|
||||
|
||||
// Check performs parse time checking on the ScalarNode so it fulfills the Node interface.
|
||||
func (n *ScalarNode) Check(*Tree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return returns the result type of the ScalarNode so it fulfills the Node interface.
|
||||
func (n *ScalarNode) Return() ReturnType {
|
||||
return TypeScalar
|
||||
}
|
||||
|
||||
// StringNode holds a string constant. The value has been "unquoted".
|
||||
type StringNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
Quoted string // The original text of the string, with quotes.
|
||||
Text string // The string, after quote processing.
|
||||
}
|
||||
|
||||
func newString(pos Pos, orig, text string) *StringNode {
|
||||
return &StringNode{NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
|
||||
}
|
||||
|
||||
// String returns the string representation of the StringNode so it fulfills the Node interface.
|
||||
func (s *StringNode) String() string {
|
||||
return s.Quoted
|
||||
}
|
||||
|
||||
// StringAST returns the string representation of abstract syntax tree of the StringNode so it fulfills the Node interface.
|
||||
func (s *StringNode) StringAST() string {
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// Check performs parse time checking on the StringNode so it fulfills the Node interface.
|
||||
func (s *StringNode) Check(*Tree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return returns the result type of the TypeString so it fulfills the Node interface.
|
||||
func (s *StringNode) Return() ReturnType {
|
||||
return TypeString
|
||||
}
|
||||
|
||||
// BinaryNode holds two arguments and an operator.
|
||||
type BinaryNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
Args [2]Node
|
||||
Operator item
|
||||
OpStr string
|
||||
}
|
||||
|
||||
func newBinary(operator item, arg1, arg2 Node) *BinaryNode {
|
||||
return &BinaryNode{NodeType: NodeBinary, Pos: operator.pos, Args: [2]Node{arg1, arg2}, Operator: operator, OpStr: operator.val}
|
||||
}
|
||||
|
||||
// String returns the string representation of the BinaryNode so it fulfills the Node interface.
|
||||
func (b *BinaryNode) String() string {
|
||||
return fmt.Sprintf("%s %s %s", b.Args[0], b.Operator.val, b.Args[1])
|
||||
}
|
||||
|
||||
// StringAST returns the string representation of abstract syntax tree of the BinaryNode so it fulfills the Node interface.
|
||||
func (b *BinaryNode) StringAST() string {
|
||||
return fmt.Sprintf("%s(%s, %s)", b.Operator.val, b.Args[0], b.Args[1])
|
||||
}
|
||||
|
||||
// Check performs parse time checking on the BinaryNode so it fulfills the Node interface.
|
||||
func (b *BinaryNode) Check(t *Tree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return returns the result type of the BinaryNode so it fulfills the Node interface.
|
||||
func (b *BinaryNode) Return() ReturnType {
|
||||
t0 := b.Args[0].Return()
|
||||
t1 := b.Args[1].Return()
|
||||
if t1 > t0 {
|
||||
return t1
|
||||
}
|
||||
return t0
|
||||
}
|
||||
|
||||
// UnaryNode holds one argument and an operator.
|
||||
type UnaryNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
Arg Node
|
||||
Operator item
|
||||
OpStr string
|
||||
}
|
||||
|
||||
func newUnary(operator item, arg Node) *UnaryNode {
|
||||
return &UnaryNode{NodeType: NodeUnary, Pos: operator.pos, Arg: arg, Operator: operator, OpStr: operator.val}
|
||||
}
|
||||
|
||||
// String returns the string representation of the UnaryNode so it fulfills the Node interface.
|
||||
func (u *UnaryNode) String() string {
|
||||
return fmt.Sprintf("%s%s", u.Operator.val, u.Arg)
|
||||
}
|
||||
|
||||
// StringAST returns the string representation of abstract syntax tree of the UnaryNode so it fulfills the Node interface.
|
||||
func (u *UnaryNode) StringAST() string {
|
||||
return fmt.Sprintf("%s(%s)", u.Operator.val, u.Arg)
|
||||
}
|
||||
|
||||
// Check performs parse time checking on the UnaryNode so it fulfills the Node interface.
|
||||
func (u *UnaryNode) Check(t *Tree) error {
|
||||
switch rt := u.Arg.Return(); rt {
|
||||
case TypeNumberSet, TypeSeriesSet, TypeScalar:
|
||||
return u.Arg.Check(t)
|
||||
default:
|
||||
return fmt.Errorf(`parse: type error in %s, expected "number", got %s`, u, rt)
|
||||
}
|
||||
}
|
||||
|
||||
// Return returns the result type of the UnaryNode so it fulfills the Node interface.
|
||||
func (u *UnaryNode) Return() ReturnType {
|
||||
return u.Arg.Return()
|
||||
}
|
||||
|
||||
// Walk invokes f on n and sub-nodes of n.
|
||||
func Walk(n Node, f func(Node)) {
|
||||
f(n)
|
||||
switch n := n.(type) {
|
||||
case *BinaryNode:
|
||||
Walk(n.Args[0], f)
|
||||
Walk(n.Args[1], f)
|
||||
case *FuncNode:
|
||||
for _, a := range n.Args {
|
||||
Walk(a, f)
|
||||
}
|
||||
case *ScalarNode, *StringNode:
|
||||
// Ignore since these node types have no sub nodes.
|
||||
case *UnaryNode:
|
||||
Walk(n.Arg, f)
|
||||
default:
|
||||
panic(fmt.Errorf("other type: %T", n))
|
||||
}
|
||||
}
|
||||
|
||||
// ReturnType represents the type that is returned from a node.
|
||||
type ReturnType int
|
||||
|
||||
const (
|
||||
// TypeString is a single string.
|
||||
TypeString ReturnType = iota
|
||||
// TypeScalar is a unlabled number constant.
|
||||
TypeScalar
|
||||
// TypeNumberSet is a collection of labelled numbers.
|
||||
TypeNumberSet
|
||||
// TypeSeriesSet is a collection of labelled time series.
|
||||
TypeSeriesSet
|
||||
// TypeVariantSet is a collection of the same type Number, Series, or Scalar.
|
||||
TypeVariantSet
|
||||
)
|
||||
|
||||
// String returns a string representation of the ReturnType.
|
||||
func (f ReturnType) String() string {
|
||||
switch f {
|
||||
case TypeNumberSet:
|
||||
return "numberSet"
|
||||
case TypeString:
|
||||
return "string"
|
||||
case TypeSeriesSet:
|
||||
return "seriesSet"
|
||||
case TypeScalar:
|
||||
return "scalar"
|
||||
case TypeVariantSet:
|
||||
return "variant"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user