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:
Kyle Brandt
2020-11-19 07:17:00 -05:00
committed by GitHub
parent f01c3f35e1
commit 0cb29d337a
44 changed files with 5770 additions and 401 deletions
+337
View File
@@ -0,0 +1,337 @@
// 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
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
// item represents a token or text string returned from the scanner.
type item struct {
typ itemType // The type of this item.
pos Pos // The starting position, in bytes, of this item in the input string.
val string // The value of this item.
}
func (i item) String() string {
switch {
case i.typ == itemEOF:
return "EOF"
case i.typ == itemError:
return i.val
case len(i.val) > 10:
return fmt.Sprintf("%.10q...", i.val)
}
return fmt.Sprintf("%q", i.val)
}
// itemType identifies the type of lex items.
type itemType int
const (
itemError itemType = iota // error occurred; value is text of error
itemEOF
itemNot // '!'
itemAnd // '&&'
itemOr // '||'
itemGreater // '>'
itemLess // '<'
itemGreaterEq // '>='
itemLessEq // '<='
itemEq // '=='
itemNotEq // '!='
itemPlus // '+'
itemMinus // '-'
itemMult // '*'
itemDiv // '/'
itemMod // '%'
itemNumber // simple number
itemComma
itemLeftParen
itemRightParen
itemString
itemFunc
itemVar // e.g. $A
itemPow // '**'
)
const eof = -1
// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*lexer) stateFn
// lexer holds the state of the scanner.
type lexer struct {
input string // the string being scanned
state stateFn // the next lexing function to enter
pos Pos // current position in the input
start Pos // start position of this item
width Pos // width of last rune read from input
lastPos Pos // position of most recent item returned by nextItem
items chan item // channel of scanned items
}
// next returns the next rune in the input.
func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) {
l.width = 0
return eof
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = Pos(w)
l.pos += l.width
return r
}
// peek returns but does not consume the next rune in the input.
// nolint:unused
func (l *lexer) peek() rune {
r := l.next()
l.backup()
return r
}
// backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() {
l.pos -= l.width
}
// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
l.items <- item{t, l.start, l.input[l.start:l.pos]}
l.start = l.pos
}
// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
if strings.ContainsRune(valid, l.next()) {
return true
}
l.backup()
return false
}
// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
for strings.ContainsRune(valid, l.next()) {
}
l.backup()
}
// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
l.start = l.pos
}
// lineNumber reports which line we're on, based on the position of
// the previous item returned by nextItem. Doing it this way
// means we don't have to worry about peek double counting.
// nolint:unused
func (l *lexer) lineNumber() int {
return 1 + strings.Count(l.input[:l.lastPos], "\n")
}
// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
return nil
}
// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
item := <-l.items
l.lastPos = item.pos
return item
}
// lex creates a new scanner for the input string.
func lex(input string) *lexer {
l := &lexer{
input: input,
items: make(chan item),
}
go l.run()
return l
}
// run runs the state machine for the lexer.
func (l *lexer) run() {
for l.state = lexItem; l.state != nil; {
l.state = l.state(l)
}
}
// state functions
func lexItem(l *lexer) stateFn {
Loop:
for {
switch r := l.next(); {
case r == '$':
return lexVar
case isSymbol(r):
return lexSymbol
case isNumber(r):
l.backup()
return lexNumber
case unicode.IsLetter(r):
return lexFunc
case r == '(':
l.emit(itemLeftParen)
case r == ')':
l.emit(itemRightParen)
case r == '"':
return lexString
case r == ',':
l.emit(itemComma)
case isSpace(r):
l.ignore()
case r == eof:
l.emit(itemEOF)
break Loop
default:
return l.errorf("invalid character: %s", string(r))
}
}
return nil
}
// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
// and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice.
func lexNumber(l *lexer) stateFn {
if !l.scanNumber() {
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
}
l.emit(itemNumber)
return lexItem
}
func (l *lexer) scanNumber() bool {
// Is it hex?
digits := "0123456789"
if l.accept("0") && l.accept("xX") {
digits = "0123456789abcdefABCDEF"
}
l.acceptRun(digits)
if l.accept(".") {
l.acceptRun(digits)
}
if l.accept("eE") {
l.accept("+-")
l.acceptRun("0123456789")
}
return true
}
const symbols = "!<>=&|+-*/%"
func lexSymbol(l *lexer) stateFn {
l.acceptRun(symbols)
s := l.input[l.start:l.pos]
switch s {
case "!":
l.emit(itemNot)
case "&&":
l.emit(itemAnd)
case "||":
l.emit(itemOr)
case ">":
l.emit(itemGreater)
case "<":
l.emit(itemLess)
case ">=":
l.emit(itemGreaterEq)
case "<=":
l.emit(itemLessEq)
case "==":
l.emit(itemEq)
case "!=":
l.emit(itemNotEq)
case "+":
l.emit(itemPlus)
case "-":
l.emit(itemMinus)
case "*":
l.emit(itemMult)
case "**":
l.emit(itemPow)
case "/":
l.emit(itemDiv)
case "%":
l.emit(itemMod)
default:
l.emit(itemError)
}
return lexItem
}
func lexFunc(l *lexer) stateFn {
for {
switch r := l.next(); {
case unicode.IsLetter(r):
// absorb
default:
l.backup()
l.emit(itemFunc)
return lexItem
}
}
}
func lexVar(l *lexer) stateFn {
hasChar := false
for {
switch r := l.next(); {
case unicode.IsLetter(r):
hasChar = true
// absorb
default:
if !hasChar {
return l.errorf("incomplete variable")
}
l.backup()
l.emit(itemVar)
return lexItem
}
}
}
func lexString(l *lexer) stateFn {
for {
switch l.next() {
case '"':
l.emit(itemString)
return lexItem
case eof:
return l.errorf("unterminated string")
}
}
}
// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
return unicode.IsSpace(r)
}
// isVarchar should maybe be used in place of unicode is letter above,
// but do not want to modify it at this time, so adding lint exception.
// nolint:unused,deadcode
func isVarchar(r rune) bool {
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}
func isSymbol(r rune) bool {
return strings.ContainsRune(symbols, r)
}
func isNumber(r rune) bool {
return unicode.IsDigit(r) || r == '.'
}
+156
View File
@@ -0,0 +1,156 @@
// 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
import (
"fmt"
"testing"
)
// Make the types prettyprint.
var itemName = map[itemType]string{
itemError: "error",
itemEOF: "EOF",
itemNot: "!",
itemAnd: "&&",
itemOr: "||",
itemGreater: ">",
itemLess: "<",
itemGreaterEq: ">=",
itemLessEq: "<=",
itemEq: "==",
itemNotEq: "!=",
itemPlus: "+",
itemMinus: "-",
itemMult: "*",
itemDiv: "/",
itemMod: "%",
itemNumber: "number",
itemComma: ",",
itemLeftParen: "(",
itemRightParen: ")",
itemString: "string",
itemFunc: "func",
}
func (i itemType) String() string {
s := itemName[i]
if s == "" {
return fmt.Sprintf("item%d", int(i))
}
return s
}
type lexTest struct {
name string
input string
items []item
}
var (
tEOF = item{itemEOF, 0, ""}
tLt = item{itemLess, 0, "<"}
tGt = item{itemGreater, 0, ">"}
tOr = item{itemOr, 0, "||"}
tNot = item{itemNot, 0, "!"}
tAnd = item{itemAnd, 0, "&&"}
tLtEq = item{itemLessEq, 0, "<="}
tGtEq = item{itemGreaterEq, 0, ">="}
tNotEq = item{itemNotEq, 0, "!="}
tEq = item{itemEq, 0, "=="}
tPlus = item{itemPlus, 0, "+"}
tMinus = item{itemMinus, 0, "-"}
tMult = item{itemMult, 0, "*"}
tDiv = item{itemDiv, 0, "/"}
tMod = item{itemMod, 0, "%"}
)
var lexTests = []lexTest{
{"empty", "", []item{tEOF}},
{"spaces", " \t\n", []item{tEOF}},
{"text", `"now is the time"`, []item{{itemString, 0, `"now is the time"`}, tEOF}},
{"operators", "! && || < > <= >= == != + - * / %", []item{
tNot,
tAnd,
tOr,
tLt,
tGt,
tLtEq,
tGtEq,
tEq,
tNotEq,
tPlus,
tMinus,
tMult,
tDiv,
tMod,
tEOF,
}},
{"numbers", "1 02 0x14 7.2 1e3 1.2e-4", []item{
{itemNumber, 0, "1"},
{itemNumber, 0, "02"},
{itemNumber, 0, "0x14"},
{itemNumber, 0, "7.2"},
{itemNumber, 0, "1e3"},
{itemNumber, 0, "1.2e-4"},
tEOF,
}},
{"number plus var", "1 + $A", []item{
{itemNumber, 0, "1"},
tPlus,
{itemVar, 0, "$A"},
tEOF,
}},
// errors
{"unclosed quote", "\"", []item{
{itemError, 0, "unterminated string"},
}},
{"single quote", "'single quote is invalid'", []item{
{itemError, 0, "invalid character: '"},
}},
{"invalid var", "$", []item{
{itemError, 0, "incomplete variable"},
}},
}
// collect gathers the emitted items into a slice.
func collect(t *lexTest) (items []item) {
l := lex(t.input)
for {
item := l.nextItem()
items = append(items, item)
if item.typ == itemEOF || item.typ == itemError {
break
}
}
return
}
func equal(i1, i2 []item, checkPos bool) bool {
if len(i1) != len(i2) {
return false
}
for k := range i1 {
if i1[k].typ != i2[k].typ {
return false
}
if i1[k].val != i2[k].val {
return false
}
if checkPos && i1[k].pos != i2[k].pos {
return false
}
}
return true
}
func TestLex(t *testing.T) {
for i, test := range lexTests {
items := collect(&lexTests[i])
if !equal(items, test.items, false) {
t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, items, test.items)
}
}
}
+422
View File
@@ -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"
}
}
+353
View File
@@ -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()
}