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,353 @@
|
||||
// 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.
|
||||
|
||||
// Package parse builds parse trees for expressions as defined by expr. Clients
|
||||
// should use that package to construct expressions rather than this one, which
|
||||
// provides shared internal data structures not intended for general use.
|
||||
package parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Tree is the representation of a single parsed expression.
|
||||
type Tree struct {
|
||||
Text string // text parsed to create the expression.
|
||||
Root Node // top-level root of the tree, returns a number.
|
||||
VarNames []string
|
||||
|
||||
funcs []map[string]Func
|
||||
|
||||
// Parsing only; cleared after parse.
|
||||
lex *lexer
|
||||
token [1]item // one-token lookahead for parser.
|
||||
peekCount int
|
||||
}
|
||||
|
||||
// Func holds the structure of a parsed function call.
|
||||
type Func struct {
|
||||
Args []ReturnType
|
||||
Return ReturnType
|
||||
F interface{}
|
||||
VariantReturn bool
|
||||
Check func(*Tree, *FuncNode) error
|
||||
}
|
||||
|
||||
// Parse returns a Tree, created by parsing the expression described in the
|
||||
// argument string. If an error is encountered, parsing stops and an empty Tree
|
||||
// is returned with the error.
|
||||
func Parse(text string, funcs ...map[string]Func) (t *Tree, err error) {
|
||||
t = New()
|
||||
t.Text = text
|
||||
err = t.Parse(text, funcs...)
|
||||
return
|
||||
}
|
||||
|
||||
// next returns the next token.
|
||||
func (t *Tree) next() item {
|
||||
if t.peekCount > 0 {
|
||||
t.peekCount--
|
||||
} else {
|
||||
t.token[0] = t.lex.nextItem()
|
||||
}
|
||||
return t.token[t.peekCount]
|
||||
}
|
||||
|
||||
// backup backs the input stream up one token.
|
||||
func (t *Tree) backup() {
|
||||
t.peekCount++
|
||||
}
|
||||
|
||||
// peek returns but does not consume the next token.
|
||||
func (t *Tree) peek() item {
|
||||
if t.peekCount > 0 {
|
||||
return t.token[t.peekCount-1]
|
||||
}
|
||||
t.peekCount = 1
|
||||
t.token[0] = t.lex.nextItem()
|
||||
return t.token[0]
|
||||
}
|
||||
|
||||
// Parsing.
|
||||
|
||||
// New allocates a new parse tree with the given name.
|
||||
func New(funcs ...map[string]Func) *Tree {
|
||||
return &Tree{
|
||||
funcs: funcs,
|
||||
}
|
||||
}
|
||||
|
||||
// errorf formats the error and terminates processing.
|
||||
func (t *Tree) errorf(format string, args ...interface{}) {
|
||||
t.Root = nil
|
||||
format = fmt.Sprintf("expr: %s", format)
|
||||
panic(fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// error terminates processing.
|
||||
func (t *Tree) error(err error) {
|
||||
t.errorf("%s", err)
|
||||
}
|
||||
|
||||
// expect consumes the next token and guarantees it has the required type.
|
||||
func (t *Tree) expect(expected itemType, context string) item {
|
||||
token := t.next()
|
||||
if token.typ != expected {
|
||||
t.unexpected(token, context)
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// expectOneOf consumes the next token and guarantees it has one of the required types.
|
||||
// nolint:unused
|
||||
func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
|
||||
token := t.next()
|
||||
if token.typ != expected1 && token.typ != expected2 {
|
||||
t.unexpected(token, context)
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// unexpected complains about the token and terminates processing.
|
||||
func (t *Tree) unexpected(token item, context string) {
|
||||
t.errorf("unexpected %s in %s", token, context)
|
||||
}
|
||||
|
||||
// recover is the handler that turns panics into returns from the top level of Parse.
|
||||
func (t *Tree) recover(errp *error) {
|
||||
e := recover()
|
||||
if e != nil {
|
||||
if _, ok := e.(runtime.Error); ok {
|
||||
panic(e)
|
||||
}
|
||||
if t != nil {
|
||||
t.stopParse()
|
||||
}
|
||||
*errp = e.(error)
|
||||
}
|
||||
}
|
||||
|
||||
// startParse initializes the parser, using the lexer.
|
||||
func (t *Tree) startParse(funcs []map[string]Func, lex *lexer) {
|
||||
t.Root = nil
|
||||
t.lex = lex
|
||||
t.funcs = funcs
|
||||
}
|
||||
|
||||
// stopParse terminates parsing.
|
||||
func (t *Tree) stopParse() {
|
||||
t.lex = nil
|
||||
}
|
||||
|
||||
// Parse parses the expression definition string to construct a representation
|
||||
// of the expression for execution.
|
||||
func (t *Tree) Parse(text string, funcs ...map[string]Func) (err error) {
|
||||
defer t.recover(&err)
|
||||
t.startParse(funcs, lex(text))
|
||||
t.Text = text
|
||||
t.parse()
|
||||
t.stopParse()
|
||||
return nil
|
||||
}
|
||||
|
||||
// parse is the top-level parser for an expression.
|
||||
// It runs to EOF.
|
||||
func (t *Tree) parse() {
|
||||
t.Root = t.O()
|
||||
t.expect(itemEOF, "root input")
|
||||
if err := t.Root.Check(t); err != nil {
|
||||
t.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
/* Grammar:
|
||||
O -> A {"||" A}
|
||||
A -> C {"&&" C}
|
||||
C -> P {( "==" | "!=" | ">" | ">=" | "<" | "<=") P}
|
||||
P -> M {( "+" | "-" ) M}
|
||||
M -> E {( "*" | "/" ) F}
|
||||
E -> F {( "**" ) F}
|
||||
F -> v | "(" O ")" | "!" O | "-" O
|
||||
v -> number | func(..) | queryVar
|
||||
Func -> name "(" param {"," param} ")"
|
||||
param -> number | "string" | queryVar
|
||||
*/
|
||||
|
||||
// expr:
|
||||
|
||||
// O is A {"||" A} in the grammar.
|
||||
func (t *Tree) O() Node {
|
||||
n := t.A()
|
||||
for {
|
||||
switch t.peek().typ {
|
||||
case itemOr:
|
||||
n = newBinary(t.next(), n, t.A())
|
||||
default:
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A is C {"&&" C} in the grammar.
|
||||
func (t *Tree) A() Node {
|
||||
n := t.C()
|
||||
for {
|
||||
switch t.peek().typ {
|
||||
case itemAnd:
|
||||
n = newBinary(t.next(), n, t.C())
|
||||
default:
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// C is C -> P {( "==" | "!=" | ">" | ">=" | "<" | "<=") P} in the grammar.
|
||||
func (t *Tree) C() Node {
|
||||
n := t.P()
|
||||
for {
|
||||
switch t.peek().typ {
|
||||
case itemEq, itemNotEq, itemGreater, itemGreaterEq, itemLess, itemLessEq:
|
||||
n = newBinary(t.next(), n, t.P())
|
||||
default:
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// P is M {( "+" | "-" ) M} in the grammar.
|
||||
func (t *Tree) P() Node {
|
||||
n := t.M()
|
||||
for {
|
||||
switch t.peek().typ {
|
||||
case itemPlus, itemMinus:
|
||||
n = newBinary(t.next(), n, t.M())
|
||||
default:
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// M is E {( "*" | "/" ) F} in the grammar.
|
||||
func (t *Tree) M() Node {
|
||||
n := t.E()
|
||||
for {
|
||||
switch t.peek().typ {
|
||||
case itemMult, itemDiv, itemMod:
|
||||
n = newBinary(t.next(), n, t.E())
|
||||
default:
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// E is F {( "**" ) F} in the grammar.
|
||||
func (t *Tree) E() Node {
|
||||
n := t.F()
|
||||
for {
|
||||
switch t.peek().typ {
|
||||
case itemPow:
|
||||
n = newBinary(t.next(), n, t.F())
|
||||
default:
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// F is v | "(" O ")" | "!" O | "-" O in the grammar.
|
||||
func (t *Tree) F() Node {
|
||||
switch token := t.peek(); token.typ {
|
||||
case itemNumber, itemFunc, itemVar:
|
||||
return t.v()
|
||||
case itemNot, itemMinus:
|
||||
return newUnary(t.next(), t.F())
|
||||
case itemLeftParen:
|
||||
t.next()
|
||||
n := t.O()
|
||||
t.expect(itemRightParen, "input: F()")
|
||||
return n
|
||||
default:
|
||||
t.unexpected(token, "input: F()")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// V is number | func(..) | queryVar in the grammar.
|
||||
func (t *Tree) v() Node {
|
||||
switch token := t.next(); token.typ {
|
||||
case itemNumber:
|
||||
n, err := newNumber(token.pos, token.val)
|
||||
if err != nil {
|
||||
t.error(err)
|
||||
}
|
||||
return n
|
||||
case itemFunc:
|
||||
t.backup()
|
||||
return t.Func()
|
||||
case itemVar:
|
||||
t.backup()
|
||||
return t.Var()
|
||||
default:
|
||||
t.unexpected(token, "input: v()")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Var is queryVar in the grammar.
|
||||
func (t *Tree) Var() (v *VarNode) {
|
||||
token := t.next()
|
||||
varNoPrefix := strings.TrimPrefix(token.val, "$")
|
||||
t.VarNames = append(t.VarNames, varNoPrefix)
|
||||
return newVar(token.pos, varNoPrefix, token.val)
|
||||
}
|
||||
|
||||
// Func parses a FuncNode.
|
||||
func (t *Tree) Func() (f *FuncNode) {
|
||||
token := t.next()
|
||||
funcv, ok := t.GetFunction(token.val)
|
||||
if !ok {
|
||||
t.errorf("non existent function %s", token.val)
|
||||
}
|
||||
f = newFunc(token.pos, token.val, funcv)
|
||||
t.expect(itemLeftParen, "func")
|
||||
for {
|
||||
switch token = t.next(); token.typ {
|
||||
default:
|
||||
t.backup()
|
||||
node := t.O()
|
||||
f.append(node)
|
||||
if len(f.Args) == 1 && f.F.VariantReturn {
|
||||
f.F.Return = node.Return()
|
||||
}
|
||||
case itemString:
|
||||
s, err := strconv.Unquote(token.val)
|
||||
if err != nil {
|
||||
t.errorf("Unquoting error: %s", err)
|
||||
}
|
||||
f.append(newString(token.pos, token.val, s))
|
||||
case itemRightParen:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetFunction gets a parsed Func from the functions available on the tree's func property.
|
||||
func (t *Tree) GetFunction(name string) (v Func, ok bool) {
|
||||
for _, funcMap := range t.funcs {
|
||||
if funcMap == nil {
|
||||
continue
|
||||
}
|
||||
if v, ok = funcMap[name]; ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// String returns a string representation of the parse tree.
|
||||
func (t *Tree) String() string {
|
||||
return t.Root.String()
|
||||
}
|
||||
Reference in New Issue
Block a user