diff --git a/vendor/github.com/expr-lang/expr/.gitignore b/vendor/github.com/expr-lang/expr/.gitignore
new file mode 100644
index 00000000..9d3410bd
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/.gitignore
@@ -0,0 +1,11 @@
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+*.test
+*.out
+*.html
+custom_tests.json
+pro/
+test/avs/
diff --git a/vendor/github.com/expr-lang/expr/LICENSE b/vendor/github.com/expr-lang/expr/LICENSE
new file mode 100644
index 00000000..ec46ce32
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Anton Medvedev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/expr-lang/expr/README.md b/vendor/github.com/expr-lang/expr/README.md
new file mode 100644
index 00000000..6c56c67b
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/README.md
@@ -0,0 +1,175 @@
+
Expr
+
+> [!IMPORTANT]
+> The repository [github.com/antonmedv/expr](https://github.com/antonmedv/expr) moved to [github.com/**expr-lang**/expr](https://github.com/expr-lang/expr).
+
+[![test](https://github.com/expr-lang/expr/actions/workflows/test.yml/badge.svg)](https://github.com/expr-lang/expr/actions/workflows/test.yml)
+[![Go Report Card](https://goreportcard.com/badge/github.com/expr-lang/expr)](https://goreportcard.com/report/github.com/expr-lang/expr)
+[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/expr.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:expr)
+[![GoDoc](https://godoc.org/github.com/expr-lang/expr?status.svg)](https://godoc.org/github.com/expr-lang/expr)
+
+**Expr** is a Go-centric expression language designed to deliver dynamic configurations with unparalleled accuracy, safety, and speed.
+**Expr** combines simple [syntax](https://expr-lang.org/docs/language-definition) with powerful features for ease of use:
+
+```js
+// Allow only admins and moderators to moderate comments.
+user.Group in ["admin", "moderator"] || user.Id == comment.UserId
+```
+
+```js
+// Determine whether the request is in the permitted time window.
+request.Time - resource.Age < duration("24h")
+```
+
+```js
+// Ensure all tweets are less than 240 characters.
+all(tweets, len(.Content) <= 240)
+```
+
+## Features
+
+**Expr** is a safe, fast, and intuitive expression evaluator optimized for the Go language.
+Here are its standout features:
+
+### Safety and Isolation
+* **Memory-Safe**: Expr is designed with a focus on safety, ensuring that programs do not access unrelated memory or introduce memory vulnerabilities.
+* **Side-Effect-Free**: Expressions evaluated in Expr only compute outputs from their inputs, ensuring no side-effects that can change state or produce unintended results.
+* **Always Terminating**: Expr is designed to prevent infinite loops, ensuring that every program will conclude in a reasonable amount of time.
+
+### Go Integration
+* **Seamless with Go**: Integrate Expr into your Go projects without the need to redefine types.
+
+### Static Typing
+* Ensures type correctness and prevents runtime type errors.
+ ```go
+ out, err := expr.Compile(`name + age`)
+ // err: invalid operation + (mismatched types string and int)
+ // | name + age
+ // | .....^
+ ```
+
+### User-Friendly
+* Provides user-friendly error messages to assist with debugging and development.
+
+### Flexibility and Utility
+* **Rich Operators**: Offers a reasonable set of basic operators for a variety of applications.
+* **Built-in Functions**: Functions like `all`, `none`, `any`, `one`, `filter`, and `map` are provided out-of-the-box.
+
+### Performance
+* **Optimized for Speed**: Expr stands out in its performance, utilizing an optimizing compiler and a bytecode virtual machine. Check out these [benchmarks](https://github.com/antonmedv/golang-expression-evaluation-comparison#readme) for more details.
+
+## Install
+
+```
+go get github.com/expr-lang/expr
+```
+
+## Documentation
+
+* See [Getting Started](https://expr-lang.org/docs/Getting-Started) page for developer documentation.
+* See [Language Definition](https://expr-lang.org/docs/language-definition) page to learn the syntax.
+
+## Examples
+
+[Play Online](https://go.dev/play/p/XCoNXEjm3TS)
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/expr-lang/expr"
+)
+
+func main() {
+ env := map[string]interface{}{
+ "greet": "Hello, %v!",
+ "names": []string{"world", "you"},
+ "sprintf": fmt.Sprintf,
+ }
+
+ code := `sprintf(greet, names[0])`
+
+ program, err := expr.Compile(code, expr.Env(env))
+ if err != nil {
+ panic(err)
+ }
+
+ output, err := expr.Run(program, env)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Println(output)
+}
+```
+
+[Play Online](https://go.dev/play/p/tz-ZneBfSuw)
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/expr-lang/expr"
+)
+
+type Tweet struct {
+ Len int
+}
+
+type Env struct {
+ Tweets []Tweet
+}
+
+func main() {
+ code := `all(Tweets, {.Len <= 240})`
+
+ program, err := expr.Compile(code, expr.Env(Env{}))
+ if err != nil {
+ panic(err)
+ }
+
+ env := Env{
+ Tweets: []Tweet{{42}, {98}, {69}},
+ }
+ output, err := expr.Run(program, env)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Println(output)
+}
+```
+
+## Who uses Expr?
+
+* [Google](https://google.com) uses Expr as one of its expression languages on the [Google Cloud Platform](https://cloud.google.com).
+* [Uber](https://uber.com) uses Expr to allow customization of its Uber Eats marketplace.
+* [GoDaddy](https://godaddy.com) employs Expr for the customization of its GoDaddy Pro product.
+* [ByteDance](https://bytedance.com) incorporates Expr into its internal business rule engine.
+* [Aviasales](https://aviasales.ru) utilizes Expr as a business rule engine for its flight search engine.
+* [Wish.com](https://www.wish.com) employs Expr in its decision-making rule engine for the Wish Assistant.
+* [Argo](https://argoproj.github.io) integrates Expr into Argo Rollouts and Argo Workflows for Kubernetes.
+* [OpenTelemetry](https://opentelemetry.io) integrates Expr into the OpenTelemetry Collector.
+* [Philips Labs](https://github.com/philips-labs/tabia) employs Expr in Tabia, a tool designed to collect insights on their code bases.
+* [CrowdSec](https://crowdsec.net) incorporates Expr into its security automation tool.
+* [CoreDNS](https://coredns.io) uses Expr in CoreDNS, which is a DNS server.
+* [qiniu](https://www.qiniu.com) implements Expr in its trade systems.
+* [Junglee Games](https://www.jungleegames.com/) uses Expr for its in-house marketing retention tool, Project Audience.
+* [Faceit](https://www.faceit.com) uses Expr to enhance customization of its eSports matchmaking algorithm.
+* [Chaos Mesh](https://chaos-mesh.org) incorporates Expr into Chaos Mesh, a cloud-native Chaos Engineering platform.
+* [Visually.io](https://visually.io) employs Expr as a business rule engine for its personalization targeting algorithm.
+* [Akvorado](https://github.com/akvorado/akvorado) utilizes Expr to classify exporters and interfaces in network flows.
+* [keda.sh](https://keda.sh) uses Expr to allow customization of its Kubernetes-based event-driven autoscaling.
+* [Span Digital](https://spandigital.com/) uses Expr in it's Knowledge Management products.
+* [Xiaohongshu](https://www.xiaohongshu.com/) combining yaml with Expr for dynamically policies delivery.
+* [Melrōse](https://melrōse.org) uses Expr to implement its music programming language.
+
+[Add your company too](https://github.com/expr-lang/expr/edit/master/README.md)
+
+## License
+
+[MIT](https://github.com/expr-lang/expr/blob/master/LICENSE)
+
+
diff --git a/vendor/github.com/expr-lang/expr/SECURITY.md b/vendor/github.com/expr-lang/expr/SECURITY.md
new file mode 100644
index 00000000..8d692a39
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/SECURITY.md
@@ -0,0 +1,25 @@
+# Security Policy
+
+## Supported Versions
+
+Expr is generally backwards compatible with very few exceptions, so we
+recommend users to always use the latest version to experience stability,
+performance and security.
+
+We generally backport security issues to a single previous minor version,
+unless this is not possible or feasible with a reasonable effort.
+
+| Version | Supported |
+|---------|--------------------|
+| 1.16 | :white_check_mark: |
+| 1.15 | :white_check_mark: |
+| 1.14 | :white_check_mark: |
+| 1.13 | :white_check_mark: |
+| < 1.13 | :x: |
+
+## Reporting a Vulnerability
+
+If you believe you've discovered a serious vulnerability, please contact the
+Expr core team at anton+security@medv.io. We will evaluate your report and if
+necessary issue a fix and an advisory. If the issue was previously undisclosed,
+we'll also mention your name in the credits.
diff --git a/vendor/github.com/expr-lang/expr/ast/dump.go b/vendor/github.com/expr-lang/expr/ast/dump.go
new file mode 100644
index 00000000..56bc7dbe
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/ast/dump.go
@@ -0,0 +1,59 @@
+package ast
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+)
+
+func Dump(node Node) string {
+ return dump(reflect.ValueOf(node), "")
+}
+
+func dump(v reflect.Value, ident string) string {
+ if !v.IsValid() {
+ return "nil"
+ }
+ t := v.Type()
+ switch t.Kind() {
+ case reflect.Struct:
+ out := t.Name() + "{\n"
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if isPrivate(f.Name) {
+ continue
+ }
+ s := v.Field(i)
+ out += fmt.Sprintf("%v%v: %v,\n", ident+"\t", f.Name, dump(s, ident+"\t"))
+ }
+ return out + ident + "}"
+ case reflect.Slice:
+ if v.Len() == 0 {
+ return t.String() + "{}"
+ }
+ out := t.String() + "{\n"
+ for i := 0; i < v.Len(); i++ {
+ s := v.Index(i)
+ out += fmt.Sprintf("%v%v,", ident+"\t", dump(s, ident+"\t"))
+ if i+1 < v.Len() {
+ out += "\n"
+ }
+ }
+ return out + "\n" + ident + "}"
+ case reflect.Ptr:
+ return dump(v.Elem(), ident)
+ case reflect.Interface:
+ return dump(reflect.ValueOf(v.Interface()), ident)
+
+ case reflect.String:
+ return fmt.Sprintf("%q", v)
+ default:
+ return fmt.Sprintf("%v", v)
+ }
+}
+
+var isCapital = regexp.MustCompile("^[A-Z]")
+
+func isPrivate(s string) bool {
+ return !isCapital.Match([]byte(s))
+}
diff --git a/vendor/github.com/expr-lang/expr/ast/node.go b/vendor/github.com/expr-lang/expr/ast/node.go
new file mode 100644
index 00000000..03e8cf62
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/ast/node.go
@@ -0,0 +1,216 @@
+package ast
+
+import (
+ "reflect"
+
+ "github.com/expr-lang/expr/file"
+)
+
+// Node represents items of abstract syntax tree.
+type Node interface {
+ Location() file.Location
+ SetLocation(file.Location)
+ Type() reflect.Type
+ SetType(reflect.Type)
+ String() string
+}
+
+// Patch replaces the node with a new one.
+// Location information is preserved.
+// Type information is lost.
+func Patch(node *Node, newNode Node) {
+ newNode.SetLocation((*node).Location())
+ *node = newNode
+}
+
+// base is a base struct for all nodes.
+type base struct {
+ loc file.Location
+ nodeType reflect.Type
+}
+
+// Location returns the location of the node in the source code.
+func (n *base) Location() file.Location {
+ return n.loc
+}
+
+// SetLocation sets the location of the node in the source code.
+func (n *base) SetLocation(loc file.Location) {
+ n.loc = loc
+}
+
+// Type returns the type of the node.
+func (n *base) Type() reflect.Type {
+ return n.nodeType
+}
+
+// SetType sets the type of the node.
+func (n *base) SetType(t reflect.Type) {
+ n.nodeType = t
+}
+
+// NilNode represents nil.
+type NilNode struct {
+ base
+}
+
+// IdentifierNode represents an identifier.
+type IdentifierNode struct {
+ base
+ Value string // Name of the identifier. Like "foo" in "foo.bar".
+}
+
+// IntegerNode represents an integer.
+type IntegerNode struct {
+ base
+ Value int // Value of the integer.
+}
+
+// FloatNode represents a float.
+type FloatNode struct {
+ base
+ Value float64 // Value of the float.
+}
+
+// BoolNode represents a boolean.
+type BoolNode struct {
+ base
+ Value bool // Value of the boolean.
+}
+
+// StringNode represents a string.
+type StringNode struct {
+ base
+ Value string // Value of the string.
+}
+
+// ConstantNode represents a constant.
+// Constants are predefined values like nil, true, false, array, map, etc.
+// The parser.Parse will never generate ConstantNode, it is only generated
+// by the optimizer.
+type ConstantNode struct {
+ base
+ Value any // Value of the constant.
+}
+
+// UnaryNode represents a unary operator.
+type UnaryNode struct {
+ base
+ Operator string // Operator of the unary operator. Like "!" in "!foo" or "not" in "not foo".
+ Node Node // Node of the unary operator. Like "foo" in "!foo".
+}
+
+// BinaryNode represents a binary operator.
+type BinaryNode struct {
+ base
+ Operator string // Operator of the binary operator. Like "+" in "foo + bar" or "matches" in "foo matches bar".
+ Left Node // Left node of the binary operator.
+ Right Node // Right node of the binary operator.
+}
+
+// ChainNode represents an optional chaining group.
+// A few MemberNode nodes can be chained together,
+// and will be wrapped in a ChainNode. Example:
+//
+// foo.bar?.baz?.qux
+//
+// The whole chain will be wrapped in a ChainNode.
+type ChainNode struct {
+ base
+ Node Node // Node of the chain.
+}
+
+// MemberNode represents a member access.
+// It can be a field access, a method call,
+// or an array element access.
+// Example:
+//
+// foo.bar or foo["bar"]
+// foo.bar()
+// array[0]
+type MemberNode struct {
+ base
+ Node Node // Node of the member access. Like "foo" in "foo.bar".
+ Property Node // Property of the member access. For property access it is a StringNode.
+ Optional bool // If true then the member access is optional. Like "foo?.bar".
+ Method bool
+}
+
+// SliceNode represents access to a slice of an array.
+// Example:
+//
+// array[1:4]
+type SliceNode struct {
+ base
+ Node Node // Node of the slice. Like "array" in "array[1:4]".
+ From Node // From an index of the array. Like "1" in "array[1:4]".
+ To Node // To an index of the array. Like "4" in "array[1:4]".
+}
+
+// CallNode represents a function or a method call.
+type CallNode struct {
+ base
+ Callee Node // Node of the call. Like "foo" in "foo()".
+ Arguments []Node // Arguments of the call.
+}
+
+// BuiltinNode represents a builtin function call.
+type BuiltinNode struct {
+ base
+ Name string // Name of the builtin function. Like "len" in "len(foo)".
+ Arguments []Node // Arguments of the builtin function.
+ Throws bool // If true then accessing a field or array index can throw an error. Used by optimizer.
+ Map Node // Used by optimizer to fold filter() and map() builtins.
+}
+
+// ClosureNode represents a predicate.
+// Example:
+//
+// filter(foo, .bar == 1)
+//
+// The predicate is ".bar == 1".
+type ClosureNode struct {
+ base
+ Node Node // Node of the predicate body.
+}
+
+// PointerNode represents a pointer to a current value in predicate.
+type PointerNode struct {
+ base
+ Name string // Name of the pointer. Like "index" in "#index".
+}
+
+// ConditionalNode represents a ternary operator.
+type ConditionalNode struct {
+ base
+ Cond Node // Condition of the ternary operator. Like "foo" in "foo ? bar : baz".
+ Exp1 Node // Expression 1 of the ternary operator. Like "bar" in "foo ? bar : baz".
+ Exp2 Node // Expression 2 of the ternary operator. Like "baz" in "foo ? bar : baz".
+}
+
+// VariableDeclaratorNode represents a variable declaration.
+type VariableDeclaratorNode struct {
+ base
+ Name string // Name of the variable. Like "foo" in "let foo = 1; foo + 1".
+ Value Node // Value of the variable. Like "1" in "let foo = 1; foo + 1".
+ Expr Node // Expression of the variable. Like "foo + 1" in "let foo = 1; foo + 1".
+}
+
+// ArrayNode represents an array.
+type ArrayNode struct {
+ base
+ Nodes []Node // Nodes of the array.
+}
+
+// MapNode represents a map.
+type MapNode struct {
+ base
+ Pairs []Node // PairNode nodes.
+}
+
+// PairNode represents a key-value pair of a map.
+type PairNode struct {
+ base
+ Key Node // Key of the pair.
+ Value Node // Value of the pair.
+}
diff --git a/vendor/github.com/expr-lang/expr/ast/print.go b/vendor/github.com/expr-lang/expr/ast/print.go
new file mode 100644
index 00000000..6a7d698a
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/ast/print.go
@@ -0,0 +1,221 @@
+package ast
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/expr-lang/expr/parser/operator"
+ "github.com/expr-lang/expr/parser/utils"
+)
+
+func (n *NilNode) String() string {
+ return "nil"
+}
+
+func (n *IdentifierNode) String() string {
+ return n.Value
+}
+
+func (n *IntegerNode) String() string {
+ return fmt.Sprintf("%d", n.Value)
+}
+
+func (n *FloatNode) String() string {
+ return fmt.Sprintf("%v", n.Value)
+}
+
+func (n *BoolNode) String() string {
+ return fmt.Sprintf("%t", n.Value)
+}
+
+func (n *StringNode) String() string {
+ return fmt.Sprintf("%q", n.Value)
+}
+
+func (n *ConstantNode) String() string {
+ if n.Value == nil {
+ return "nil"
+ }
+ b, err := json.Marshal(n.Value)
+ if err != nil {
+ panic(err)
+ }
+ return string(b)
+}
+
+func (n *UnaryNode) String() string {
+ op := ""
+ if n.Operator == "not" {
+ op = fmt.Sprintf("%s ", n.Operator)
+ } else {
+ op = fmt.Sprintf("%s", n.Operator)
+ }
+ if _, ok := n.Node.(*BinaryNode); ok {
+ return fmt.Sprintf("%s(%s)", op, n.Node.String())
+ }
+ return fmt.Sprintf("%s%s", op, n.Node.String())
+}
+
+func (n *BinaryNode) String() string {
+ if n.Operator == ".." {
+ return fmt.Sprintf("%s..%s", n.Left, n.Right)
+ }
+
+ var lhs, rhs string
+ var lwrap, rwrap bool
+
+ if lb, ok := n.Left.(*BinaryNode); ok {
+ if operator.Less(lb.Operator, n.Operator) {
+ lwrap = true
+ }
+ if lb.Operator == "??" {
+ lwrap = true
+ }
+ if operator.IsBoolean(lb.Operator) && n.Operator != lb.Operator {
+ lwrap = true
+ }
+ }
+ if rb, ok := n.Right.(*BinaryNode); ok {
+ if operator.Less(rb.Operator, n.Operator) {
+ rwrap = true
+ }
+ if operator.IsBoolean(rb.Operator) && n.Operator != rb.Operator {
+ rwrap = true
+ }
+ }
+
+ if _, ok := n.Left.(*ConditionalNode); ok {
+ lwrap = true
+ }
+ if _, ok := n.Right.(*ConditionalNode); ok {
+ rwrap = true
+ }
+
+ if lwrap {
+ lhs = fmt.Sprintf("(%s)", n.Left.String())
+ } else {
+ lhs = n.Left.String()
+ }
+
+ if rwrap {
+ rhs = fmt.Sprintf("(%s)", n.Right.String())
+ } else {
+ rhs = n.Right.String()
+ }
+
+ return fmt.Sprintf("%s %s %s", lhs, n.Operator, rhs)
+}
+
+func (n *ChainNode) String() string {
+ return n.Node.String()
+}
+
+func (n *MemberNode) String() string {
+ node := n.Node.String()
+ if _, ok := n.Node.(*BinaryNode); ok {
+ node = fmt.Sprintf("(%s)", node)
+ }
+
+ if n.Optional {
+ if str, ok := n.Property.(*StringNode); ok && utils.IsValidIdentifier(str.Value) {
+ return fmt.Sprintf("%s?.%s", node, str.Value)
+ } else {
+ return fmt.Sprintf("%s?.[%s]", node, n.Property.String())
+ }
+ }
+ if str, ok := n.Property.(*StringNode); ok && utils.IsValidIdentifier(str.Value) {
+ if _, ok := n.Node.(*PointerNode); ok {
+ return fmt.Sprintf(".%s", str.Value)
+ }
+ return fmt.Sprintf("%s.%s", node, str.Value)
+ }
+ return fmt.Sprintf("%s[%s]", node, n.Property.String())
+}
+
+func (n *SliceNode) String() string {
+ if n.From == nil && n.To == nil {
+ return fmt.Sprintf("%s[:]", n.Node.String())
+ }
+ if n.From == nil {
+ return fmt.Sprintf("%s[:%s]", n.Node.String(), n.To.String())
+ }
+ if n.To == nil {
+ return fmt.Sprintf("%s[%s:]", n.Node.String(), n.From.String())
+ }
+ return fmt.Sprintf("%s[%s:%s]", n.Node.String(), n.From.String(), n.To.String())
+}
+
+func (n *CallNode) String() string {
+ arguments := make([]string, len(n.Arguments))
+ for i, arg := range n.Arguments {
+ arguments[i] = arg.String()
+ }
+ return fmt.Sprintf("%s(%s)", n.Callee.String(), strings.Join(arguments, ", "))
+}
+
+func (n *BuiltinNode) String() string {
+ arguments := make([]string, len(n.Arguments))
+ for i, arg := range n.Arguments {
+ arguments[i] = arg.String()
+ }
+ return fmt.Sprintf("%s(%s)", n.Name, strings.Join(arguments, ", "))
+}
+
+func (n *ClosureNode) String() string {
+ return n.Node.String()
+}
+
+func (n *PointerNode) String() string {
+ return fmt.Sprintf("#%s", n.Name)
+}
+
+func (n *VariableDeclaratorNode) String() string {
+ return fmt.Sprintf("let %s = %s; %s", n.Name, n.Value.String(), n.Expr.String())
+}
+
+func (n *ConditionalNode) String() string {
+ var cond, exp1, exp2 string
+ if _, ok := n.Cond.(*ConditionalNode); ok {
+ cond = fmt.Sprintf("(%s)", n.Cond.String())
+ } else {
+ cond = n.Cond.String()
+ }
+ if _, ok := n.Exp1.(*ConditionalNode); ok {
+ exp1 = fmt.Sprintf("(%s)", n.Exp1.String())
+ } else {
+ exp1 = n.Exp1.String()
+ }
+ if _, ok := n.Exp2.(*ConditionalNode); ok {
+ exp2 = fmt.Sprintf("(%s)", n.Exp2.String())
+ } else {
+ exp2 = n.Exp2.String()
+ }
+ return fmt.Sprintf("%s ? %s : %s", cond, exp1, exp2)
+}
+
+func (n *ArrayNode) String() string {
+ nodes := make([]string, len(n.Nodes))
+ for i, node := range n.Nodes {
+ nodes[i] = node.String()
+ }
+ return fmt.Sprintf("[%s]", strings.Join(nodes, ", "))
+}
+
+func (n *MapNode) String() string {
+ pairs := make([]string, len(n.Pairs))
+ for i, pair := range n.Pairs {
+ pairs[i] = pair.String()
+ }
+ return fmt.Sprintf("{%s}", strings.Join(pairs, ", "))
+}
+
+func (n *PairNode) String() string {
+ if str, ok := n.Key.(*StringNode); ok {
+ if utils.IsValidIdentifier(str.Value) {
+ return fmt.Sprintf("%s: %s", str.Value, n.Value.String())
+ }
+ return fmt.Sprintf("%s: %s", str.String(), n.Value.String())
+ }
+ return fmt.Sprintf("(%s): %s", n.Key.String(), n.Value.String())
+}
diff --git a/vendor/github.com/expr-lang/expr/ast/visitor.go b/vendor/github.com/expr-lang/expr/ast/visitor.go
new file mode 100644
index 00000000..90bc9f1d
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/ast/visitor.go
@@ -0,0 +1,74 @@
+package ast
+
+import "fmt"
+
+type Visitor interface {
+ Visit(node *Node)
+}
+
+func Walk(node *Node, v Visitor) {
+ if *node == nil {
+ return
+ }
+ switch n := (*node).(type) {
+ case *NilNode:
+ case *IdentifierNode:
+ case *IntegerNode:
+ case *FloatNode:
+ case *BoolNode:
+ case *StringNode:
+ case *ConstantNode:
+ case *UnaryNode:
+ Walk(&n.Node, v)
+ case *BinaryNode:
+ Walk(&n.Left, v)
+ Walk(&n.Right, v)
+ case *ChainNode:
+ Walk(&n.Node, v)
+ case *MemberNode:
+ Walk(&n.Node, v)
+ Walk(&n.Property, v)
+ case *SliceNode:
+ Walk(&n.Node, v)
+ if n.From != nil {
+ Walk(&n.From, v)
+ }
+ if n.To != nil {
+ Walk(&n.To, v)
+ }
+ case *CallNode:
+ Walk(&n.Callee, v)
+ for i := range n.Arguments {
+ Walk(&n.Arguments[i], v)
+ }
+ case *BuiltinNode:
+ for i := range n.Arguments {
+ Walk(&n.Arguments[i], v)
+ }
+ case *ClosureNode:
+ Walk(&n.Node, v)
+ case *PointerNode:
+ case *VariableDeclaratorNode:
+ Walk(&n.Value, v)
+ Walk(&n.Expr, v)
+ case *ConditionalNode:
+ Walk(&n.Cond, v)
+ Walk(&n.Exp1, v)
+ Walk(&n.Exp2, v)
+ case *ArrayNode:
+ for i := range n.Nodes {
+ Walk(&n.Nodes[i], v)
+ }
+ case *MapNode:
+ for i := range n.Pairs {
+ Walk(&n.Pairs[i], v)
+ }
+ case *PairNode:
+ Walk(&n.Key, v)
+ Walk(&n.Value, v)
+ default:
+ panic(fmt.Sprintf("undefined node type (%T)", node))
+ }
+
+ v.Visit(node)
+}
diff --git a/vendor/github.com/expr-lang/expr/builtin/builtin.go b/vendor/github.com/expr-lang/expr/builtin/builtin.go
new file mode 100644
index 00000000..cc6f197c
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/builtin/builtin.go
@@ -0,0 +1,982 @@
+package builtin
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/expr-lang/expr/internal/deref"
+ "github.com/expr-lang/expr/vm/runtime"
+)
+
+var (
+ Index map[string]int
+ Names []string
+)
+
+func init() {
+ Index = make(map[string]int)
+ Names = make([]string, len(Builtins))
+ for i, fn := range Builtins {
+ Index[fn.Name] = i
+ Names[i] = fn.Name
+ }
+}
+
+var Builtins = []*Function{
+ {
+ Name: "all",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "none",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "any",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "one",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "filter",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) []any)),
+ },
+ {
+ Name: "map",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) any) []any)),
+ },
+ {
+ Name: "find",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) any)),
+ },
+ {
+ Name: "findIndex",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) int)),
+ },
+ {
+ Name: "findLast",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) any)),
+ },
+ {
+ Name: "findLastIndex",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) int)),
+ },
+ {
+ Name: "count",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) int)),
+ },
+ {
+ Name: "sum",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) int)),
+ },
+ {
+ Name: "groupBy",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) any) map[any][]any)),
+ },
+ {
+ Name: "sortBy",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool, string) []any)),
+ },
+ {
+ Name: "reduce",
+ Predicate: true,
+ Types: types(new(func([]any, func(any, any) any, any) any)),
+ },
+ {
+ Name: "len",
+ Fast: Len,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String, reflect.Interface:
+ return integerType, nil
+ }
+ return anyType, fmt.Errorf("invalid argument for len (type %s)", args[0])
+ },
+ },
+ {
+ Name: "type",
+ Fast: Type,
+ Types: types(new(func(any) string)),
+ },
+ {
+ Name: "abs",
+ Fast: Abs,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Interface:
+ return args[0], nil
+ }
+ return anyType, fmt.Errorf("invalid argument for abs (type %s)", args[0])
+ },
+ },
+ {
+ Name: "ceil",
+ Fast: Ceil,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ return validateRoundFunc("ceil", args)
+ },
+ },
+ {
+ Name: "floor",
+ Fast: Floor,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ return validateRoundFunc("floor", args)
+ },
+ },
+ {
+ Name: "round",
+ Fast: Round,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ return validateRoundFunc("round", args)
+ },
+ },
+ {
+ Name: "int",
+ Fast: Int,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return integerType, nil
+ case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return integerType, nil
+ case reflect.String:
+ return integerType, nil
+ }
+ return anyType, fmt.Errorf("invalid argument for int (type %s)", args[0])
+ },
+ },
+ {
+ Name: "float",
+ Fast: Float,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return floatType, nil
+ case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return floatType, nil
+ case reflect.String:
+ return floatType, nil
+ }
+ return anyType, fmt.Errorf("invalid argument for float (type %s)", args[0])
+ },
+ },
+ {
+ Name: "string",
+ Fast: String,
+ Types: types(new(func(any any) string)),
+ },
+ {
+ Name: "trim",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 1 {
+ return strings.TrimSpace(args[0].(string)), nil
+ } else if len(args) == 2 {
+ return strings.Trim(args[0].(string), args[1].(string)), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for trim (expected 1 or 2, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.TrimSpace,
+ strings.Trim,
+ ),
+ },
+ {
+ Name: "trimPrefix",
+ Func: func(args ...any) (any, error) {
+ s := " "
+ if len(args) == 2 {
+ s = args[1].(string)
+ }
+ return strings.TrimPrefix(args[0].(string), s), nil
+ },
+ Types: types(
+ strings.TrimPrefix,
+ new(func(string) string),
+ ),
+ },
+ {
+ Name: "trimSuffix",
+ Func: func(args ...any) (any, error) {
+ s := " "
+ if len(args) == 2 {
+ s = args[1].(string)
+ }
+ return strings.TrimSuffix(args[0].(string), s), nil
+ },
+ Types: types(
+ strings.TrimSuffix,
+ new(func(string) string),
+ ),
+ },
+ {
+ Name: "upper",
+ Fast: func(arg any) any {
+ return strings.ToUpper(arg.(string))
+ },
+ Types: types(strings.ToUpper),
+ },
+ {
+ Name: "lower",
+ Fast: func(arg any) any {
+ return strings.ToLower(arg.(string))
+ },
+ Types: types(strings.ToLower),
+ },
+ {
+ Name: "split",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 2 {
+ return strings.Split(args[0].(string), args[1].(string)), nil
+ } else if len(args) == 3 {
+ return strings.SplitN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for split (expected 2 or 3, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.Split,
+ strings.SplitN,
+ ),
+ },
+ {
+ Name: "splitAfter",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 2 {
+ return strings.SplitAfter(args[0].(string), args[1].(string)), nil
+ } else if len(args) == 3 {
+ return strings.SplitAfterN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for splitAfter (expected 2 or 3, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.SplitAfter,
+ strings.SplitAfterN,
+ ),
+ },
+ {
+ Name: "replace",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 4 {
+ return strings.Replace(args[0].(string), args[1].(string), args[2].(string), runtime.ToInt(args[3])), nil
+ } else if len(args) == 3 {
+ return strings.ReplaceAll(args[0].(string), args[1].(string), args[2].(string)), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for replace (expected 3 or 4, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.Replace,
+ strings.ReplaceAll,
+ ),
+ },
+ {
+ Name: "repeat",
+ Safe: func(args ...any) (any, uint, error) {
+ s := args[0].(string)
+ n := runtime.ToInt(args[1])
+ if n < 0 {
+ return nil, 0, fmt.Errorf("invalid argument for repeat (expected positive integer, got %d)", n)
+ }
+ if n > 1e6 {
+ return nil, 0, fmt.Errorf("memory budget exceeded")
+ }
+ return strings.Repeat(s, n), uint(len(s) * n), nil
+ },
+ Types: types(strings.Repeat),
+ },
+ {
+ Name: "join",
+ Func: func(args ...any) (any, error) {
+ glue := ""
+ if len(args) == 2 {
+ glue = args[1].(string)
+ }
+ switch args[0].(type) {
+ case []string:
+ return strings.Join(args[0].([]string), glue), nil
+ case []any:
+ var s []string
+ for _, arg := range args[0].([]any) {
+ s = append(s, arg.(string))
+ }
+ return strings.Join(s, glue), nil
+ }
+ return nil, fmt.Errorf("invalid argument for join (type %s)", reflect.TypeOf(args[0]))
+ },
+ Types: types(
+ strings.Join,
+ new(func([]any, string) string),
+ new(func([]any) string),
+ new(func([]string, string) string),
+ new(func([]string) string),
+ ),
+ },
+ {
+ Name: "indexOf",
+ Func: func(args ...any) (any, error) {
+ return strings.Index(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.Index),
+ },
+ {
+ Name: "lastIndexOf",
+ Func: func(args ...any) (any, error) {
+ return strings.LastIndex(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.LastIndex),
+ },
+ {
+ Name: "hasPrefix",
+ Func: func(args ...any) (any, error) {
+ return strings.HasPrefix(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.HasPrefix),
+ },
+ {
+ Name: "hasSuffix",
+ Func: func(args ...any) (any, error) {
+ return strings.HasSuffix(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.HasSuffix),
+ },
+ {
+ Name: "max",
+ Func: func(args ...any) (any, error) {
+ return minMax("max", runtime.Less, args...)
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ return validateAggregateFunc("max", args)
+ },
+ },
+ {
+ Name: "min",
+ Func: func(args ...any) (any, error) {
+ return minMax("min", runtime.More, args...)
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ return validateAggregateFunc("min", args)
+ },
+ },
+ {
+ Name: "mean",
+ Func: func(args ...any) (any, error) {
+ count, sum, err := mean(args...)
+ if err != nil {
+ return nil, err
+ }
+ if count == 0 {
+ return 0.0, nil
+ }
+ return sum / float64(count), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ return validateAggregateFunc("mean", args)
+ },
+ },
+ {
+ Name: "median",
+ Func: func(args ...any) (any, error) {
+ values, err := median(args...)
+ if err != nil {
+ return nil, err
+ }
+ if n := len(values); n > 0 {
+ sort.Float64s(values)
+ if n%2 == 1 {
+ return values[n/2], nil
+ }
+ return (values[n/2-1] + values[n/2]) / 2, nil
+ }
+ return 0.0, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ return validateAggregateFunc("median", args)
+ },
+ },
+ {
+ Name: "toJSON",
+ Func: func(args ...any) (any, error) {
+ b, err := json.MarshalIndent(args[0], "", " ")
+ if err != nil {
+ return nil, err
+ }
+ return string(b), nil
+ },
+ Types: types(new(func(any) string)),
+ },
+ {
+ Name: "fromJSON",
+ Func: func(args ...any) (any, error) {
+ var v any
+ err := json.Unmarshal([]byte(args[0].(string)), &v)
+ if err != nil {
+ return nil, err
+ }
+ return v, nil
+ },
+ Types: types(new(func(string) any)),
+ },
+ {
+ Name: "toBase64",
+ Func: func(args ...any) (any, error) {
+ return base64.StdEncoding.EncodeToString([]byte(args[0].(string))), nil
+ },
+ Types: types(new(func(string) string)),
+ },
+ {
+ Name: "fromBase64",
+ Func: func(args ...any) (any, error) {
+ b, err := base64.StdEncoding.DecodeString(args[0].(string))
+ if err != nil {
+ return nil, err
+ }
+ return string(b), nil
+ },
+ Types: types(new(func(string) string)),
+ },
+ {
+ Name: "now",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 0 {
+ return time.Now(), nil
+ }
+ if len(args) == 1 {
+ if tz, ok := args[0].(*time.Location); ok {
+ return time.Now().In(tz), nil
+ }
+ }
+ return nil, fmt.Errorf("invalid number of arguments (expected 0, got %d)", len(args))
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) == 0 {
+ return timeType, nil
+ }
+ if len(args) == 1 {
+ if args[0] != nil && args[0].AssignableTo(locationType) {
+ return timeType, nil
+ }
+ }
+ return anyType, fmt.Errorf("invalid number of arguments (expected 0, got %d)", len(args))
+ },
+ },
+ {
+ Name: "duration",
+ Func: func(args ...any) (any, error) {
+ return time.ParseDuration(args[0].(string))
+ },
+ Types: types(time.ParseDuration),
+ },
+ {
+ Name: "date",
+ Func: func(args ...any) (any, error) {
+ tz, ok := args[0].(*time.Location)
+ if ok {
+ args = args[1:]
+ }
+
+ date := args[0].(string)
+ if len(args) == 2 {
+ layout := args[1].(string)
+ if tz != nil {
+ return time.ParseInLocation(layout, date, tz)
+ }
+ return time.Parse(layout, date)
+ }
+ if len(args) == 3 {
+ layout := args[1].(string)
+ timeZone := args[2].(string)
+ tz, err := time.LoadLocation(timeZone)
+ if err != nil {
+ return nil, err
+ }
+ t, err := time.ParseInLocation(layout, date, tz)
+ if err != nil {
+ return nil, err
+ }
+ return t, nil
+ }
+
+ layouts := []string{
+ "2006-01-02",
+ "15:04:05",
+ "2006-01-02 15:04:05",
+ time.RFC3339,
+ time.RFC822,
+ time.RFC850,
+ time.RFC1123,
+ }
+ for _, layout := range layouts {
+ if tz == nil {
+ t, err := time.Parse(layout, date)
+ if err == nil {
+ return t, nil
+ }
+ } else {
+ t, err := time.ParseInLocation(layout, date, tz)
+ if err == nil {
+ return t, nil
+ }
+ }
+ }
+ return nil, fmt.Errorf("invalid date %s", date)
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) < 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected at least 1, got %d)", len(args))
+ }
+ if args[0] != nil && args[0].AssignableTo(locationType) {
+ args = args[1:]
+ }
+ if len(args) > 3 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected at most 3, got %d)", len(args))
+ }
+ return timeType, nil
+ },
+ },
+ {
+ Name: "timezone",
+ Func: func(args ...any) (any, error) {
+ tz, err := time.LoadLocation(args[0].(string))
+ if err != nil {
+ return nil, err
+ }
+ return tz, nil
+ },
+ Types: types(time.LoadLocation),
+ },
+ {
+ Name: "first",
+ Func: func(args ...any) (any, error) {
+ defer func() {
+ if r := recover(); r != nil {
+ return
+ }
+ }()
+ return runtime.Fetch(args[0], 0), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return anyType, nil
+ case reflect.Slice, reflect.Array:
+ return args[0].Elem(), nil
+ }
+ return anyType, fmt.Errorf("cannot get first element from %s", args[0])
+ },
+ },
+ {
+ Name: "last",
+ Func: func(args ...any) (any, error) {
+ defer func() {
+ if r := recover(); r != nil {
+ return
+ }
+ }()
+ return runtime.Fetch(args[0], -1), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return anyType, nil
+ case reflect.Slice, reflect.Array:
+ return args[0].Elem(), nil
+ }
+ return anyType, fmt.Errorf("cannot get last element from %s", args[0])
+ },
+ },
+ {
+ Name: "get",
+ Func: func(args ...any) (out any, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ return
+ }
+ }()
+ return runtime.Fetch(args[0], args[1]), nil
+ },
+ },
+ {
+ Name: "take",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 2 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 2, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot take from %s", v.Kind())
+ }
+ n := reflect.ValueOf(args[1])
+ if !n.CanInt() {
+ return nil, fmt.Errorf("cannot take %s elements", n.Kind())
+ }
+ if n.Int() > int64(v.Len()) {
+ return args[0], nil
+ }
+ return v.Slice(0, int(n.Int())).Interface(), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 2 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 2, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot take from %s", args[0])
+ }
+ switch kind(args[1]) {
+ case reflect.Interface, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ default:
+ return anyType, fmt.Errorf("cannot take %s elements", args[1])
+ }
+ return args[0], nil
+ },
+ },
+ {
+ Name: "keys",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Map {
+ return nil, fmt.Errorf("cannot get keys from %s", v.Kind())
+ }
+ keys := v.MapKeys()
+ out := make([]any, len(keys))
+ for i, key := range keys {
+ out[i] = key.Interface()
+ }
+ return out, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return arrayType, nil
+ case reflect.Map:
+ return arrayType, nil
+ }
+ return anyType, fmt.Errorf("cannot get keys from %s", args[0])
+ },
+ },
+ {
+ Name: "values",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Map {
+ return nil, fmt.Errorf("cannot get values from %s", v.Kind())
+ }
+ keys := v.MapKeys()
+ out := make([]any, len(keys))
+ for i, key := range keys {
+ out[i] = v.MapIndex(key).Interface()
+ }
+ return out, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return arrayType, nil
+ case reflect.Map:
+ return arrayType, nil
+ }
+ return anyType, fmt.Errorf("cannot get values from %s", args[0])
+ },
+ },
+ {
+ Name: "toPairs",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Map {
+ return nil, fmt.Errorf("cannot transform %s to pairs", v.Kind())
+ }
+ keys := v.MapKeys()
+ out := make([][2]any, len(keys))
+ for i, key := range keys {
+ out[i] = [2]any{key.Interface(), v.MapIndex(key).Interface()}
+ }
+ return out, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Map:
+ return arrayType, nil
+ }
+ return anyType, fmt.Errorf("cannot transform %s to pairs", args[0])
+ },
+ },
+ {
+ Name: "fromPairs",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot transform %s from pairs", v)
+ }
+ out := reflect.MakeMap(mapType)
+ for i := 0; i < v.Len(); i++ {
+ pair := deref.Value(v.Index(i))
+ if pair.Kind() != reflect.Array && pair.Kind() != reflect.Slice {
+ return nil, fmt.Errorf("invalid pair %v", pair)
+ }
+ if pair.Len() != 2 {
+ return nil, fmt.Errorf("invalid pair length %v", pair)
+ }
+ key := pair.Index(0)
+ value := pair.Index(1)
+ out.SetMapIndex(key, value)
+ }
+ return out.Interface(), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ return mapType, nil
+ }
+ return anyType, fmt.Errorf("cannot transform %s from pairs", args[0])
+ },
+ },
+ {
+ Name: "reverse",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot reverse %s", v.Kind())
+ }
+
+ size := v.Len()
+ arr := make([]any, size)
+
+ for i := 0; i < size; i++ {
+ arr[i] = v.Index(size - i - 1).Interface()
+ }
+
+ return arr, nil
+
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ return arrayType, nil
+ default:
+ return anyType, fmt.Errorf("cannot reverse %s", args[0])
+ }
+ },
+ },
+ {
+ Name: "concat",
+ Safe: func(args ...any) (any, uint, error) {
+ if len(args) == 0 {
+ return nil, 0, fmt.Errorf("invalid number of arguments (expected at least 1, got 0)")
+ }
+
+ var size uint
+ var arr []any
+
+ for _, arg := range args {
+ v := reflect.ValueOf(deref.Deref(arg))
+
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, 0, fmt.Errorf("cannot concat %s", v.Kind())
+ }
+
+ size += uint(v.Len())
+
+ for i := 0; i < v.Len(); i++ {
+ item := v.Index(i)
+ arr = append(arr, item.Interface())
+ }
+ }
+
+ return arr, size, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) == 0 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected at least 1, got 0)")
+ }
+
+ for _, arg := range args {
+ switch kind(deref.Type(arg)) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot concat %s", arg)
+ }
+ }
+
+ return arrayType, nil
+ },
+ },
+ {
+ Name: "sort",
+ Safe: func(args ...any) (any, uint, error) {
+ if len(args) != 1 && len(args) != 2 {
+ return nil, 0, fmt.Errorf("invalid number of arguments (expected 1 or 2, got %d)", len(args))
+ }
+
+ var array []any
+
+ switch in := args[0].(type) {
+ case []any:
+ array = make([]any, len(in))
+ copy(array, in)
+ case []int:
+ array = make([]any, len(in))
+ for i, v := range in {
+ array[i] = v
+ }
+ case []float64:
+ array = make([]any, len(in))
+ for i, v := range in {
+ array[i] = v
+ }
+ case []string:
+ array = make([]any, len(in))
+ for i, v := range in {
+ array[i] = v
+ }
+ }
+
+ var desc bool
+ if len(args) == 2 {
+ switch args[1].(string) {
+ case "asc":
+ desc = false
+ case "desc":
+ desc = true
+ default:
+ return nil, 0, fmt.Errorf("invalid order %s, expected asc or desc", args[1])
+ }
+ }
+
+ sortable := &runtime.Sort{
+ Desc: desc,
+ Array: array,
+ }
+ sort.Sort(sortable)
+
+ return sortable.Array, uint(len(array)), nil
+ },
+ Types: types(
+ new(func([]any, string) []any),
+ new(func([]int, string) []any),
+ new(func([]float64, string) []any),
+ new(func([]string, string) []any),
+
+ new(func([]any) []any),
+ new(func([]float64) []any),
+ new(func([]string) []any),
+ new(func([]int) []any),
+ ),
+ },
+ bitFunc("bitand", func(x, y int) (any, error) {
+ return x & y, nil
+ }),
+ bitFunc("bitor", func(x, y int) (any, error) {
+ return x | y, nil
+ }),
+ bitFunc("bitxor", func(x, y int) (any, error) {
+ return x ^ y, nil
+ }),
+ bitFunc("bitnand", func(x, y int) (any, error) {
+ return x &^ y, nil
+ }),
+ bitFunc("bitshl", func(x, y int) (any, error) {
+ if y < 0 {
+ return nil, fmt.Errorf("invalid operation: negative shift count %d (type int)", y)
+ }
+ return x << y, nil
+ }),
+ bitFunc("bitshr", func(x, y int) (any, error) {
+ if y < 0 {
+ return nil, fmt.Errorf("invalid operation: negative shift count %d (type int)", y)
+ }
+ return x >> y, nil
+ }),
+ bitFunc("bitushr", func(x, y int) (any, error) {
+ if y < 0 {
+ return nil, fmt.Errorf("invalid operation: negative shift count %d (type int)", y)
+ }
+ return int(uint(x) >> y), nil
+ }),
+ {
+ Name: "bitnot",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments for bitnot (expected 1, got %d)", len(args))
+ }
+ x, err := toInt(args[0])
+ if err != nil {
+ return nil, fmt.Errorf("%v to call bitnot", err)
+ }
+ return ^x, nil
+ },
+ Types: types(new(func(int) int)),
+ },
+}
diff --git a/vendor/github.com/expr-lang/expr/builtin/function.go b/vendor/github.com/expr-lang/expr/builtin/function.go
new file mode 100644
index 00000000..d4d78b1c
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/builtin/function.go
@@ -0,0 +1,22 @@
+package builtin
+
+import (
+ "reflect"
+)
+
+type Function struct {
+ Name string
+ Fast func(arg any) any
+ Func func(args ...any) (any, error)
+ Safe func(args ...any) (any, uint, error)
+ Types []reflect.Type
+ Validate func(args []reflect.Type) (reflect.Type, error)
+ Predicate bool
+}
+
+func (f *Function) Type() reflect.Type {
+ if len(f.Types) > 0 {
+ return f.Types[0]
+ }
+ return reflect.TypeOf(f.Func)
+}
diff --git a/vendor/github.com/expr-lang/expr/builtin/lib.go b/vendor/github.com/expr-lang/expr/builtin/lib.go
new file mode 100644
index 00000000..e3cd61b9
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/builtin/lib.go
@@ -0,0 +1,361 @@
+package builtin
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "strconv"
+
+ "github.com/expr-lang/expr/internal/deref"
+)
+
+func Len(x any) any {
+ v := reflect.ValueOf(x)
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+ return v.Len()
+ default:
+ panic(fmt.Sprintf("invalid argument for len (type %T)", x))
+ }
+}
+
+func Type(arg any) any {
+ if arg == nil {
+ return "nil"
+ }
+ v := reflect.ValueOf(arg)
+ for {
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ } else if v.Kind() == reflect.Interface {
+ v = v.Elem()
+ } else {
+ break
+ }
+ }
+ if v.Type().Name() != "" && v.Type().PkgPath() != "" {
+ return fmt.Sprintf("%s.%s", v.Type().PkgPath(), v.Type().Name())
+ }
+ switch v.Type().Kind() {
+ case reflect.Invalid:
+ return "invalid"
+ case reflect.Bool:
+ return "bool"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return "int"
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return "uint"
+ case reflect.Float32, reflect.Float64:
+ return "float"
+ case reflect.String:
+ return "string"
+ case reflect.Array, reflect.Slice:
+ return "array"
+ case reflect.Map:
+ return "map"
+ case reflect.Func:
+ return "func"
+ case reflect.Struct:
+ return "struct"
+ default:
+ return "unknown"
+ }
+}
+
+func Abs(x any) any {
+ switch x := x.(type) {
+ case float32:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case float64:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case int:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case int8:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case int16:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case int32:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case int64:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case uint:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case uint8:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case uint16:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case uint32:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ case uint64:
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ }
+ panic(fmt.Sprintf("invalid argument for abs (type %T)", x))
+}
+
+func Ceil(x any) any {
+ switch x := x.(type) {
+ case float32:
+ return math.Ceil(float64(x))
+ case float64:
+ return math.Ceil(x)
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+ return Float(x)
+ }
+ panic(fmt.Sprintf("invalid argument for ceil (type %T)", x))
+}
+
+func Floor(x any) any {
+ switch x := x.(type) {
+ case float32:
+ return math.Floor(float64(x))
+ case float64:
+ return math.Floor(x)
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+ return Float(x)
+ }
+ panic(fmt.Sprintf("invalid argument for floor (type %T)", x))
+}
+
+func Round(x any) any {
+ switch x := x.(type) {
+ case float32:
+ return math.Round(float64(x))
+ case float64:
+ return math.Round(x)
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+ return Float(x)
+ }
+ panic(fmt.Sprintf("invalid argument for round (type %T)", x))
+}
+
+func Int(x any) any {
+ switch x := x.(type) {
+ case float32:
+ return int(x)
+ case float64:
+ return int(x)
+ case int:
+ return x
+ case int8:
+ return int(x)
+ case int16:
+ return int(x)
+ case int32:
+ return int(x)
+ case int64:
+ return int(x)
+ case uint:
+ return int(x)
+ case uint8:
+ return int(x)
+ case uint16:
+ return int(x)
+ case uint32:
+ return int(x)
+ case uint64:
+ return int(x)
+ case string:
+ i, err := strconv.Atoi(x)
+ if err != nil {
+ panic(fmt.Sprintf("invalid operation: int(%s)", x))
+ }
+ return i
+ default:
+ val := reflect.ValueOf(x)
+ if val.CanConvert(integerType) {
+ return val.Convert(integerType).Interface()
+ }
+ panic(fmt.Sprintf("invalid operation: int(%T)", x))
+ }
+}
+
+func Float(x any) any {
+ switch x := x.(type) {
+ case float32:
+ return float64(x)
+ case float64:
+ return x
+ case int:
+ return float64(x)
+ case int8:
+ return float64(x)
+ case int16:
+ return float64(x)
+ case int32:
+ return float64(x)
+ case int64:
+ return float64(x)
+ case uint:
+ return float64(x)
+ case uint8:
+ return float64(x)
+ case uint16:
+ return float64(x)
+ case uint32:
+ return float64(x)
+ case uint64:
+ return float64(x)
+ case string:
+ f, err := strconv.ParseFloat(x, 64)
+ if err != nil {
+ panic(fmt.Sprintf("invalid operation: float(%s)", x))
+ }
+ return f
+ default:
+ panic(fmt.Sprintf("invalid operation: float(%T)", x))
+ }
+}
+
+func String(arg any) any {
+ return fmt.Sprintf("%v", arg)
+}
+
+func minMax(name string, fn func(any, any) bool, args ...any) (any, error) {
+ var val any
+ for _, arg := range args {
+ rv := reflect.ValueOf(deref.Deref(arg))
+ switch rv.Kind() {
+ case reflect.Array, reflect.Slice:
+ size := rv.Len()
+ for i := 0; i < size; i++ {
+ elemVal, err := minMax(name, fn, rv.Index(i).Interface())
+ if err != nil {
+ return nil, err
+ }
+ switch elemVal.(type) {
+ case int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64,
+ float32, float64:
+ if elemVal != nil && (val == nil || fn(val, elemVal)) {
+ val = elemVal
+ }
+ default:
+ return nil, fmt.Errorf("invalid argument for %s (type %T)", name, elemVal)
+ }
+
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+ reflect.Float32, reflect.Float64:
+ elemVal := rv.Interface()
+ if val == nil || fn(val, elemVal) {
+ val = elemVal
+ }
+ default:
+ if len(args) == 1 {
+ return args[0], nil
+ }
+ return nil, fmt.Errorf("invalid argument for %s (type %T)", name, arg)
+ }
+ }
+ return val, nil
+}
+
+func mean(args ...any) (int, float64, error) {
+ var total float64
+ var count int
+
+ for _, arg := range args {
+ rv := reflect.ValueOf(deref.Deref(arg))
+ switch rv.Kind() {
+ case reflect.Array, reflect.Slice:
+ size := rv.Len()
+ for i := 0; i < size; i++ {
+ elemCount, elemSum, err := mean(rv.Index(i).Interface())
+ if err != nil {
+ return 0, 0, err
+ }
+ total += elemSum
+ count += elemCount
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ total += float64(rv.Int())
+ count++
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ total += float64(rv.Uint())
+ count++
+ case reflect.Float32, reflect.Float64:
+ total += rv.Float()
+ count++
+ default:
+ return 0, 0, fmt.Errorf("invalid argument for mean (type %T)", arg)
+ }
+ }
+ return count, total, nil
+}
+
+func median(args ...any) ([]float64, error) {
+ var values []float64
+
+ for _, arg := range args {
+ rv := reflect.ValueOf(deref.Deref(arg))
+ switch rv.Kind() {
+ case reflect.Array, reflect.Slice:
+ size := rv.Len()
+ for i := 0; i < size; i++ {
+ elems, err := median(rv.Index(i).Interface())
+ if err != nil {
+ return nil, err
+ }
+ values = append(values, elems...)
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ values = append(values, float64(rv.Int()))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ values = append(values, float64(rv.Uint()))
+ case reflect.Float32, reflect.Float64:
+ values = append(values, rv.Float())
+ default:
+ return nil, fmt.Errorf("invalid argument for median (type %T)", arg)
+ }
+ }
+ return values, nil
+}
diff --git a/vendor/github.com/expr-lang/expr/builtin/utils.go b/vendor/github.com/expr-lang/expr/builtin/utils.go
new file mode 100644
index 00000000..29a95731
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/builtin/utils.go
@@ -0,0 +1,87 @@
+package builtin
+
+import (
+ "fmt"
+ "reflect"
+ "time"
+)
+
+var (
+ anyType = reflect.TypeOf(new(any)).Elem()
+ integerType = reflect.TypeOf(0)
+ floatType = reflect.TypeOf(float64(0))
+ arrayType = reflect.TypeOf([]any{})
+ mapType = reflect.TypeOf(map[any]any{})
+ timeType = reflect.TypeOf(new(time.Time)).Elem()
+ locationType = reflect.TypeOf(new(time.Location))
+)
+
+func kind(t reflect.Type) reflect.Kind {
+ if t == nil {
+ return reflect.Invalid
+ }
+ return t.Kind()
+}
+
+func types(types ...any) []reflect.Type {
+ ts := make([]reflect.Type, len(types))
+ for i, t := range types {
+ t := reflect.TypeOf(t)
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() != reflect.Func {
+ panic("not a function")
+ }
+ ts[i] = t
+ }
+ return ts
+}
+
+func toInt(val any) (int, error) {
+ switch v := val.(type) {
+ case int:
+ return v, nil
+ case int8:
+ return int(v), nil
+ case int16:
+ return int(v), nil
+ case int32:
+ return int(v), nil
+ case int64:
+ return int(v), nil
+ case uint:
+ return int(v), nil
+ case uint8:
+ return int(v), nil
+ case uint16:
+ return int(v), nil
+ case uint32:
+ return int(v), nil
+ case uint64:
+ return int(v), nil
+ default:
+ return 0, fmt.Errorf("cannot use %T as argument (type int)", val)
+ }
+}
+
+func bitFunc(name string, fn func(x, y int) (any, error)) *Function {
+ return &Function{
+ Name: name,
+ Func: func(args ...any) (any, error) {
+ if len(args) != 2 {
+ return nil, fmt.Errorf("invalid number of arguments for %s (expected 2, got %d)", name, len(args))
+ }
+ x, err := toInt(args[0])
+ if err != nil {
+ return nil, fmt.Errorf("%v to call %s", err, name)
+ }
+ y, err := toInt(args[1])
+ if err != nil {
+ return nil, fmt.Errorf("%v to call %s", err, name)
+ }
+ return fn(x, y)
+ },
+ Types: types(new(func(int, int) int)),
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/builtin/validation.go b/vendor/github.com/expr-lang/expr/builtin/validation.go
new file mode 100644
index 00000000..057f247e
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/builtin/validation.go
@@ -0,0 +1,38 @@
+package builtin
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/expr-lang/expr/internal/deref"
+)
+
+func validateAggregateFunc(name string, args []reflect.Type) (reflect.Type, error) {
+ switch len(args) {
+ case 0:
+ return anyType, fmt.Errorf("not enough arguments to call %s", name)
+ default:
+ for _, arg := range args {
+ switch kind(deref.Type(arg)) {
+ case reflect.Interface, reflect.Array, reflect.Slice:
+ return anyType, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
+ default:
+ return anyType, fmt.Errorf("invalid argument for %s (type %s)", name, arg)
+ }
+ }
+ return args[0], nil
+ }
+}
+
+func validateRoundFunc(name string, args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Interface:
+ return floatType, nil
+ default:
+ return anyType, fmt.Errorf("invalid argument for %s (type %s)", name, args[0])
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/checker/checker.go b/vendor/github.com/expr-lang/expr/checker/checker.go
new file mode 100644
index 00000000..c71a98f0
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/checker/checker.go
@@ -0,0 +1,1209 @@
+package checker
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+
+ "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/conf"
+ "github.com/expr-lang/expr/file"
+ "github.com/expr-lang/expr/internal/deref"
+ "github.com/expr-lang/expr/parser"
+)
+
+// ParseCheck parses input expression and checks its types. Also, it applies
+// all provided patchers. In case of error, it returns error with a tree.
+func ParseCheck(input string, config *conf.Config) (*parser.Tree, error) {
+ tree, err := parser.ParseWithConfig(input, config)
+ if err != nil {
+ return tree, err
+ }
+
+ if len(config.Visitors) > 0 {
+ for i := 0; i < 1000; i++ {
+ more := false
+ for _, v := range config.Visitors {
+ // We need to perform types check, because some visitors may rely on
+ // types information available in the tree.
+ _, _ = Check(tree, config)
+
+ ast.Walk(&tree.Node, v)
+
+ if v, ok := v.(interface {
+ ShouldRepeat() bool
+ }); ok {
+ more = more || v.ShouldRepeat()
+ }
+ }
+ if !more {
+ break
+ }
+ }
+ }
+ _, err = Check(tree, config)
+ if err != nil {
+ return tree, err
+ }
+
+ return tree, nil
+}
+
+// Check checks types of the expression tree. It returns type of the expression
+// and error if any. If config is nil, then default configuration will be used.
+func Check(tree *parser.Tree, config *conf.Config) (t reflect.Type, err error) {
+ if config == nil {
+ config = conf.New(nil)
+ }
+
+ v := &checker{config: config}
+
+ t, _ = v.visit(tree.Node)
+
+ if v.err != nil {
+ return t, v.err.Bind(tree.Source)
+ }
+
+ if v.config.Expect != reflect.Invalid {
+ if v.config.ExpectAny {
+ if isAny(t) {
+ return t, nil
+ }
+ }
+
+ switch v.config.Expect {
+ case reflect.Int, reflect.Int64, reflect.Float64:
+ if !isNumber(t) {
+ return nil, fmt.Errorf("expected %v, but got %v", v.config.Expect, t)
+ }
+ default:
+ if t != nil {
+ if t.Kind() == v.config.Expect {
+ return t, nil
+ }
+ }
+ return nil, fmt.Errorf("expected %v, but got %v", v.config.Expect, t)
+ }
+ }
+
+ return t, nil
+}
+
+type checker struct {
+ config *conf.Config
+ predicateScopes []predicateScope
+ varScopes []varScope
+ err *file.Error
+}
+
+type predicateScope struct {
+ vtype reflect.Type
+ vars map[string]reflect.Type
+}
+
+type varScope struct {
+ name string
+ vtype reflect.Type
+ info info
+}
+
+type info struct {
+ method bool
+ fn *builtin.Function
+
+ // elem is element type of array or map.
+ // Arrays created with type []any, but
+ // we would like to detect expressions
+ // like `42 in ["a"]` as invalid.
+ elem reflect.Type
+}
+
+func (v *checker) visit(node ast.Node) (reflect.Type, info) {
+ var t reflect.Type
+ var i info
+ switch n := node.(type) {
+ case *ast.NilNode:
+ t, i = v.NilNode(n)
+ case *ast.IdentifierNode:
+ t, i = v.IdentifierNode(n)
+ case *ast.IntegerNode:
+ t, i = v.IntegerNode(n)
+ case *ast.FloatNode:
+ t, i = v.FloatNode(n)
+ case *ast.BoolNode:
+ t, i = v.BoolNode(n)
+ case *ast.StringNode:
+ t, i = v.StringNode(n)
+ case *ast.ConstantNode:
+ t, i = v.ConstantNode(n)
+ case *ast.UnaryNode:
+ t, i = v.UnaryNode(n)
+ case *ast.BinaryNode:
+ t, i = v.BinaryNode(n)
+ case *ast.ChainNode:
+ t, i = v.ChainNode(n)
+ case *ast.MemberNode:
+ t, i = v.MemberNode(n)
+ case *ast.SliceNode:
+ t, i = v.SliceNode(n)
+ case *ast.CallNode:
+ t, i = v.CallNode(n)
+ case *ast.BuiltinNode:
+ t, i = v.BuiltinNode(n)
+ case *ast.ClosureNode:
+ t, i = v.ClosureNode(n)
+ case *ast.PointerNode:
+ t, i = v.PointerNode(n)
+ case *ast.VariableDeclaratorNode:
+ t, i = v.VariableDeclaratorNode(n)
+ case *ast.ConditionalNode:
+ t, i = v.ConditionalNode(n)
+ case *ast.ArrayNode:
+ t, i = v.ArrayNode(n)
+ case *ast.MapNode:
+ t, i = v.MapNode(n)
+ case *ast.PairNode:
+ t, i = v.PairNode(n)
+ default:
+ panic(fmt.Sprintf("undefined node type (%T)", node))
+ }
+ node.SetType(t)
+ return t, i
+}
+
+func (v *checker) error(node ast.Node, format string, args ...any) (reflect.Type, info) {
+ if v.err == nil { // show first error
+ v.err = &file.Error{
+ Location: node.Location(),
+ Message: fmt.Sprintf(format, args...),
+ }
+ }
+ return anyType, info{} // interface represent undefined type
+}
+
+func (v *checker) NilNode(*ast.NilNode) (reflect.Type, info) {
+ return nilType, info{}
+}
+
+func (v *checker) IdentifierNode(node *ast.IdentifierNode) (reflect.Type, info) {
+ if s, ok := v.lookupVariable(node.Value); ok {
+ return s.vtype, s.info
+ }
+ if node.Value == "$env" {
+ return mapType, info{}
+ }
+ return v.ident(node, node.Value, true, true)
+}
+
+// ident method returns type of environment variable, builtin or function.
+func (v *checker) ident(node ast.Node, name string, strict, builtins bool) (reflect.Type, info) {
+ if t, ok := v.config.Types[name]; ok {
+ if t.Ambiguous {
+ return v.error(node, "ambiguous identifier %v", name)
+ }
+ return t.Type, info{method: t.Method}
+ }
+ if builtins {
+ if fn, ok := v.config.Functions[name]; ok {
+ return fn.Type(), info{fn: fn}
+ }
+ if fn, ok := v.config.Builtins[name]; ok {
+ return fn.Type(), info{fn: fn}
+ }
+ }
+ if v.config.Strict && strict {
+ return v.error(node, "unknown name %v", name)
+ }
+ if v.config.DefaultType != nil {
+ return v.config.DefaultType, info{}
+ }
+ return anyType, info{}
+}
+
+func (v *checker) IntegerNode(*ast.IntegerNode) (reflect.Type, info) {
+ return integerType, info{}
+}
+
+func (v *checker) FloatNode(*ast.FloatNode) (reflect.Type, info) {
+ return floatType, info{}
+}
+
+func (v *checker) BoolNode(*ast.BoolNode) (reflect.Type, info) {
+ return boolType, info{}
+}
+
+func (v *checker) StringNode(*ast.StringNode) (reflect.Type, info) {
+ return stringType, info{}
+}
+
+func (v *checker) ConstantNode(node *ast.ConstantNode) (reflect.Type, info) {
+ return reflect.TypeOf(node.Value), info{}
+}
+
+func (v *checker) UnaryNode(node *ast.UnaryNode) (reflect.Type, info) {
+ t, _ := v.visit(node.Node)
+ t = deref.Type(t)
+
+ switch node.Operator {
+
+ case "!", "not":
+ if isBool(t) {
+ return boolType, info{}
+ }
+ if isAny(t) {
+ return boolType, info{}
+ }
+
+ case "+", "-":
+ if isNumber(t) {
+ return t, info{}
+ }
+ if isAny(t) {
+ return anyType, info{}
+ }
+
+ default:
+ return v.error(node, "unknown operator (%v)", node.Operator)
+ }
+
+ return v.error(node, `invalid operation: %v (mismatched type %v)`, node.Operator, t)
+}
+
+func (v *checker) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
+ l, _ := v.visit(node.Left)
+ r, ri := v.visit(node.Right)
+
+ l = deref.Type(l)
+ r = deref.Type(r)
+
+ switch node.Operator {
+ case "==", "!=":
+ if isComparable(l, r) {
+ return boolType, info{}
+ }
+
+ case "or", "||", "and", "&&":
+ if isBool(l) && isBool(r) {
+ return boolType, info{}
+ }
+ if or(l, r, isBool) {
+ return boolType, info{}
+ }
+
+ case "<", ">", ">=", "<=":
+ if isNumber(l) && isNumber(r) {
+ return boolType, info{}
+ }
+ if isString(l) && isString(r) {
+ return boolType, info{}
+ }
+ if isTime(l) && isTime(r) {
+ return boolType, info{}
+ }
+ if or(l, r, isNumber, isString, isTime) {
+ return boolType, info{}
+ }
+
+ case "-":
+ if isNumber(l) && isNumber(r) {
+ return combined(l, r), info{}
+ }
+ if isTime(l) && isTime(r) {
+ return durationType, info{}
+ }
+ if isTime(l) && isDuration(r) {
+ return timeType, info{}
+ }
+ if or(l, r, isNumber, isTime) {
+ return anyType, info{}
+ }
+
+ case "*":
+ if isNumber(l) && isNumber(r) {
+ return combined(l, r), info{}
+ }
+ if or(l, r, isNumber) {
+ return anyType, info{}
+ }
+
+ case "/":
+ if isNumber(l) && isNumber(r) {
+ return floatType, info{}
+ }
+ if or(l, r, isNumber) {
+ return floatType, info{}
+ }
+
+ case "**", "^":
+ if isNumber(l) && isNumber(r) {
+ return floatType, info{}
+ }
+ if or(l, r, isNumber) {
+ return floatType, info{}
+ }
+
+ case "%":
+ if isInteger(l) && isInteger(r) {
+ return combined(l, r), info{}
+ }
+ if or(l, r, isInteger) {
+ return anyType, info{}
+ }
+
+ case "+":
+ if isNumber(l) && isNumber(r) {
+ return combined(l, r), info{}
+ }
+ if isString(l) && isString(r) {
+ return stringType, info{}
+ }
+ if isTime(l) && isDuration(r) {
+ return timeType, info{}
+ }
+ if isDuration(l) && isTime(r) {
+ return timeType, info{}
+ }
+ if or(l, r, isNumber, isString, isTime, isDuration) {
+ return anyType, info{}
+ }
+
+ case "in":
+ if (isString(l) || isAny(l)) && isStruct(r) {
+ return boolType, info{}
+ }
+ if isMap(r) {
+ if l == nil { // It is possible to compare with nil.
+ return boolType, info{}
+ }
+ if !isAny(l) && !l.AssignableTo(r.Key()) {
+ return v.error(node, "cannot use %v as type %v in map key", l, r.Key())
+ }
+ return boolType, info{}
+ }
+ if isArray(r) {
+ if l == nil { // It is possible to compare with nil.
+ return boolType, info{}
+ }
+ if !isComparable(l, r.Elem()) {
+ return v.error(node, "cannot use %v as type %v in array", l, r.Elem())
+ }
+ if !isComparable(l, ri.elem) {
+ return v.error(node, "cannot use %v as type %v in array", l, ri.elem)
+ }
+ return boolType, info{}
+ }
+ if isAny(l) && anyOf(r, isString, isArray, isMap) {
+ return boolType, info{}
+ }
+ if isAny(r) {
+ return boolType, info{}
+ }
+
+ case "matches":
+ if s, ok := node.Right.(*ast.StringNode); ok {
+ _, err := regexp.Compile(s.Value)
+ if err != nil {
+ return v.error(node, err.Error())
+ }
+ }
+ if isString(l) && isString(r) {
+ return boolType, info{}
+ }
+ if or(l, r, isString) {
+ return boolType, info{}
+ }
+
+ case "contains", "startsWith", "endsWith":
+ if isString(l) && isString(r) {
+ return boolType, info{}
+ }
+ if or(l, r, isString) {
+ return boolType, info{}
+ }
+
+ case "..":
+ ret := reflect.SliceOf(integerType)
+ if isInteger(l) && isInteger(r) {
+ return ret, info{}
+ }
+ if or(l, r, isInteger) {
+ return ret, info{}
+ }
+
+ case "??":
+ if l == nil && r != nil {
+ return r, info{}
+ }
+ if l != nil && r == nil {
+ return l, info{}
+ }
+ if l == nil && r == nil {
+ return nilType, info{}
+ }
+ if r.AssignableTo(l) {
+ return l, info{}
+ }
+ return anyType, info{}
+
+ default:
+ return v.error(node, "unknown operator (%v)", node.Operator)
+
+ }
+
+ return v.error(node, `invalid operation: %v (mismatched types %v and %v)`, node.Operator, l, r)
+}
+
+func (v *checker) ChainNode(node *ast.ChainNode) (reflect.Type, info) {
+ return v.visit(node.Node)
+}
+
+func (v *checker) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
+ // $env variable
+ if an, ok := node.Node.(*ast.IdentifierNode); ok && an.Value == "$env" {
+ if name, ok := node.Property.(*ast.StringNode); ok {
+ strict := v.config.Strict
+ if node.Optional {
+ // If user explicitly set optional flag, then we should not
+ // throw error if field is not found (as user trying to handle
+ // this case). But if user did not set optional flag, then we
+ // should throw error if field is not found & v.config.Strict.
+ strict = false
+ }
+ return v.ident(node, name.Value, strict, false /* no builtins and no functions */)
+ }
+ return anyType, info{}
+ }
+
+ base, _ := v.visit(node.Node)
+ prop, _ := v.visit(node.Property)
+
+ if name, ok := node.Property.(*ast.StringNode); ok {
+ if base == nil {
+ return v.error(node, "type %v has no field %v", base, name.Value)
+ }
+ // First, check methods defined on base type itself,
+ // independent of which type it is. Without dereferencing.
+ if m, ok := base.MethodByName(name.Value); ok {
+ if kind(base) == reflect.Interface {
+ // In case of interface type method will not have a receiver,
+ // and to prevent checker decreasing numbers of in arguments
+ // return method type as not method (second argument is false).
+
+ // Also, we can not use m.Index here, because it will be
+ // different indexes for different types which implement
+ // the same interface.
+ return m.Type, info{}
+ } else {
+ return m.Type, info{method: true}
+ }
+ }
+ }
+
+ if kind(base) == reflect.Ptr {
+ base = base.Elem()
+ }
+
+ switch kind(base) {
+ case reflect.Interface:
+ return anyType, info{}
+
+ case reflect.Map:
+ if prop != nil && !prop.AssignableTo(base.Key()) && !isAny(prop) {
+ return v.error(node.Property, "cannot use %v to get an element from %v", prop, base)
+ }
+ return base.Elem(), info{}
+
+ case reflect.Array, reflect.Slice:
+ if !isInteger(prop) && !isAny(prop) {
+ return v.error(node.Property, "array elements can only be selected using an integer (got %v)", prop)
+ }
+ return base.Elem(), info{}
+
+ case reflect.Struct:
+ if name, ok := node.Property.(*ast.StringNode); ok {
+ propertyName := name.Value
+ if field, ok := fetchField(base, propertyName); ok {
+ return field.Type, info{}
+ }
+ if node.Method {
+ return v.error(node, "type %v has no method %v", base, propertyName)
+ }
+ return v.error(node, "type %v has no field %v", base, propertyName)
+ }
+ }
+
+ return v.error(node, "type %v[%v] is undefined", base, prop)
+}
+
+func (v *checker) SliceNode(node *ast.SliceNode) (reflect.Type, info) {
+ t, _ := v.visit(node.Node)
+
+ switch kind(t) {
+ case reflect.Interface:
+ // ok
+ case reflect.String, reflect.Array, reflect.Slice:
+ // ok
+ default:
+ return v.error(node, "cannot slice %v", t)
+ }
+
+ if node.From != nil {
+ from, _ := v.visit(node.From)
+ if !isInteger(from) && !isAny(from) {
+ return v.error(node.From, "non-integer slice index %v", from)
+ }
+ }
+ if node.To != nil {
+ to, _ := v.visit(node.To)
+ if !isInteger(to) && !isAny(to) {
+ return v.error(node.To, "non-integer slice index %v", to)
+ }
+ }
+ return t, info{}
+}
+
+func (v *checker) CallNode(node *ast.CallNode) (reflect.Type, info) {
+ t, i := v.functionReturnType(node)
+
+ // Check if type was set on node (for example, by patcher)
+ // and use node type instead of function return type.
+ //
+ // If node type is anyType, then we should use function
+ // return type. For example, on error we return anyType
+ // for a call `errCall().Method()` and method will be
+ // evaluated on `anyType.Method()`, so return type will
+ // be anyType `anyType.Method(): anyType`. Patcher can
+ // fix `errCall()` to return proper type, so on second
+ // checker pass we should replace anyType on method node
+ // with new correct function return type.
+ if node.Type() != nil && node.Type() != anyType {
+ return node.Type(), i
+ }
+
+ return t, i
+}
+
+func (v *checker) functionReturnType(node *ast.CallNode) (reflect.Type, info) {
+ fn, fnInfo := v.visit(node.Callee)
+
+ if fnInfo.fn != nil {
+ return v.checkFunction(fnInfo.fn, node, node.Arguments)
+ }
+
+ fnName := "function"
+ if identifier, ok := node.Callee.(*ast.IdentifierNode); ok {
+ fnName = identifier.Value
+ }
+ if member, ok := node.Callee.(*ast.MemberNode); ok {
+ if name, ok := member.Property.(*ast.StringNode); ok {
+ fnName = name.Value
+ }
+ }
+
+ if fn == nil {
+ return v.error(node, "%v is nil; cannot call nil as function", fnName)
+ }
+
+ switch fn.Kind() {
+ case reflect.Interface:
+ return anyType, info{}
+ case reflect.Func:
+ outType, err := v.checkArguments(fnName, fn, fnInfo.method, node.Arguments, node)
+ if err != nil {
+ if v.err == nil {
+ v.err = err
+ }
+ return anyType, info{}
+ }
+ return outType, info{}
+ }
+ return v.error(node, "%v is not callable", fn)
+}
+
+func (v *checker) BuiltinNode(node *ast.BuiltinNode) (reflect.Type, info) {
+ switch node.Name {
+ case "all", "none", "any", "one":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ return boolType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "filter":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ if isAny(collection) {
+ return arrayType, info{}
+ }
+ return arrayType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "map":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection, scopeVar{"index", integerType})
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ return arrayType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "count":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ if len(node.Arguments) == 1 {
+ return integerType, info{}
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+
+ return integerType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "sum":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ if len(node.Arguments) == 2 {
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+ return closure.Out(0), info{}
+ }
+ } else {
+ if isAny(collection) {
+ return anyType, info{}
+ }
+ return collection.Elem(), info{}
+ }
+
+ case "find", "findLast":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ if isAny(collection) {
+ return anyType, info{}
+ }
+ return collection.Elem(), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "findIndex", "findLastIndex":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ return integerType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "groupBy":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ return reflect.TypeOf(map[any][]any{}), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "sortBy":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if len(node.Arguments) == 3 {
+ _, _ = v.visit(node.Arguments[2])
+ }
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ return reflect.TypeOf([]any{}), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "reduce":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection, scopeVar{"index", integerType}, scopeVar{"acc", anyType})
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if len(node.Arguments) == 3 {
+ _, _ = v.visit(node.Arguments[2])
+ }
+
+ if isFunc(closure) && closure.NumOut() == 1 {
+ return closure.Out(0), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has two input and one output param")
+
+ }
+
+ if id, ok := builtin.Index[node.Name]; ok {
+ switch node.Name {
+ case "get":
+ return v.checkBuiltinGet(node)
+ }
+ return v.checkFunction(builtin.Builtins[id], node, node.Arguments)
+ }
+
+ return v.error(node, "unknown builtin %v", node.Name)
+}
+
+type scopeVar struct {
+ name string
+ vtype reflect.Type
+}
+
+func (v *checker) begin(vtype reflect.Type, vars ...scopeVar) {
+ scope := predicateScope{vtype: vtype, vars: make(map[string]reflect.Type)}
+ for _, v := range vars {
+ scope.vars[v.name] = v.vtype
+ }
+ v.predicateScopes = append(v.predicateScopes, scope)
+}
+
+func (v *checker) end() {
+ v.predicateScopes = v.predicateScopes[:len(v.predicateScopes)-1]
+}
+
+func (v *checker) checkBuiltinGet(node *ast.BuiltinNode) (reflect.Type, info) {
+ if len(node.Arguments) != 2 {
+ return v.error(node, "invalid number of arguments (expected 2, got %d)", len(node.Arguments))
+ }
+
+ val := node.Arguments[0]
+ prop := node.Arguments[1]
+ if id, ok := val.(*ast.IdentifierNode); ok && id.Value == "$env" {
+ if s, ok := prop.(*ast.StringNode); ok {
+ return v.config.Types[s.Value].Type, info{}
+ }
+ return anyType, info{}
+ }
+
+ t, _ := v.visit(val)
+
+ switch kind(t) {
+ case reflect.Interface:
+ return anyType, info{}
+ case reflect.Slice, reflect.Array:
+ p, _ := v.visit(prop)
+ if p == nil {
+ return v.error(prop, "cannot use nil as slice index")
+ }
+ if !isInteger(p) && !isAny(p) {
+ return v.error(prop, "non-integer slice index %v", p)
+ }
+ return t.Elem(), info{}
+ case reflect.Map:
+ p, _ := v.visit(prop)
+ if p == nil {
+ return v.error(prop, "cannot use nil as map index")
+ }
+ if !p.AssignableTo(t.Key()) && !isAny(p) {
+ return v.error(prop, "cannot use %v to get an element from %v", p, t)
+ }
+ return t.Elem(), info{}
+ }
+ return v.error(val, "type %v does not support indexing", t)
+}
+
+func (v *checker) checkFunction(f *builtin.Function, node ast.Node, arguments []ast.Node) (reflect.Type, info) {
+ if f.Validate != nil {
+ args := make([]reflect.Type, len(arguments))
+ for i, arg := range arguments {
+ args[i], _ = v.visit(arg)
+ }
+ t, err := f.Validate(args)
+ if err != nil {
+ return v.error(node, "%v", err)
+ }
+ return t, info{}
+ } else if len(f.Types) == 0 {
+ t, err := v.checkArguments(f.Name, f.Type(), false, arguments, node)
+ if err != nil {
+ if v.err == nil {
+ v.err = err
+ }
+ return anyType, info{}
+ }
+ // No type was specified, so we assume the function returns any.
+ return t, info{}
+ }
+ var lastErr *file.Error
+ for _, t := range f.Types {
+ outType, err := v.checkArguments(f.Name, t, false, arguments, node)
+ if err != nil {
+ lastErr = err
+ continue
+ }
+ return outType, info{}
+ }
+ if lastErr != nil {
+ if v.err == nil {
+ v.err = lastErr
+ }
+ return anyType, info{}
+ }
+
+ return v.error(node, "no matching overload for %v", f.Name)
+}
+
+func (v *checker) checkArguments(
+ name string,
+ fn reflect.Type,
+ method bool,
+ arguments []ast.Node,
+ node ast.Node,
+) (reflect.Type, *file.Error) {
+ if isAny(fn) {
+ return anyType, nil
+ }
+
+ if fn.NumOut() == 0 {
+ return anyType, &file.Error{
+ Location: node.Location(),
+ Message: fmt.Sprintf("func %v doesn't return value", name),
+ }
+ }
+ if numOut := fn.NumOut(); numOut > 2 {
+ return anyType, &file.Error{
+ Location: node.Location(),
+ Message: fmt.Sprintf("func %v returns more then two values", name),
+ }
+ }
+
+ // If func is method on an env, first argument should be a receiver,
+ // and actual arguments less than fnNumIn by one.
+ fnNumIn := fn.NumIn()
+ if method {
+ fnNumIn--
+ }
+ // Skip first argument in case of the receiver.
+ fnInOffset := 0
+ if method {
+ fnInOffset = 1
+ }
+
+ var err *file.Error
+ if fn.IsVariadic() {
+ if len(arguments) < fnNumIn-1 {
+ err = &file.Error{
+ Location: node.Location(),
+ Message: fmt.Sprintf("not enough arguments to call %v", name),
+ }
+ }
+ } else {
+ if len(arguments) > fnNumIn {
+ err = &file.Error{
+ Location: node.Location(),
+ Message: fmt.Sprintf("too many arguments to call %v", name),
+ }
+ }
+ if len(arguments) < fnNumIn {
+ err = &file.Error{
+ Location: node.Location(),
+ Message: fmt.Sprintf("not enough arguments to call %v", name),
+ }
+ }
+ }
+
+ if err != nil {
+ // If we have an error, we should still visit all arguments to
+ // type check them, as a patch can fix the error later.
+ for _, arg := range arguments {
+ _, _ = v.visit(arg)
+ }
+ return fn.Out(0), err
+ }
+
+ for i, arg := range arguments {
+ t, _ := v.visit(arg)
+
+ var in reflect.Type
+ if fn.IsVariadic() && i >= fnNumIn-1 {
+ // For variadic arguments fn(xs ...int), go replaces type of xs (int) with ([]int).
+ // As we compare arguments one by one, we need underling type.
+ in = fn.In(fn.NumIn() - 1).Elem()
+ } else {
+ in = fn.In(i + fnInOffset)
+ }
+
+ if isFloat(in) && isInteger(t) {
+ traverseAndReplaceIntegerNodesWithFloatNodes(&arguments[i], in)
+ continue
+ }
+
+ if isInteger(in) && isInteger(t) && kind(t) != kind(in) {
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&arguments[i], in)
+ continue
+ }
+
+ if t == nil {
+ continue
+ }
+
+ if !(t.AssignableTo(in) || deref.Type(t).AssignableTo(in)) && kind(t) != reflect.Interface {
+ return anyType, &file.Error{
+ Location: arg.Location(),
+ Message: fmt.Sprintf("cannot use %v as argument (type %v) to call %v ", t, in, name),
+ }
+ }
+ }
+
+ return fn.Out(0), nil
+}
+
+func traverseAndReplaceIntegerNodesWithFloatNodes(node *ast.Node, newType reflect.Type) {
+ switch (*node).(type) {
+ case *ast.IntegerNode:
+ *node = &ast.FloatNode{Value: float64((*node).(*ast.IntegerNode).Value)}
+ (*node).SetType(newType)
+ case *ast.UnaryNode:
+ unaryNode := (*node).(*ast.UnaryNode)
+ traverseAndReplaceIntegerNodesWithFloatNodes(&unaryNode.Node, newType)
+ case *ast.BinaryNode:
+ binaryNode := (*node).(*ast.BinaryNode)
+ switch binaryNode.Operator {
+ case "+", "-", "*":
+ traverseAndReplaceIntegerNodesWithFloatNodes(&binaryNode.Left, newType)
+ traverseAndReplaceIntegerNodesWithFloatNodes(&binaryNode.Right, newType)
+ }
+ }
+}
+
+func traverseAndReplaceIntegerNodesWithIntegerNodes(node *ast.Node, newType reflect.Type) {
+ switch (*node).(type) {
+ case *ast.IntegerNode:
+ (*node).SetType(newType)
+ case *ast.UnaryNode:
+ (*node).SetType(newType)
+ unaryNode := (*node).(*ast.UnaryNode)
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&unaryNode.Node, newType)
+ case *ast.BinaryNode:
+ // TODO: Binary node return type is dependent on the type of the operands. We can't just change the type of the node.
+ binaryNode := (*node).(*ast.BinaryNode)
+ switch binaryNode.Operator {
+ case "+", "-", "*":
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&binaryNode.Left, newType)
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&binaryNode.Right, newType)
+ }
+ }
+}
+
+func (v *checker) ClosureNode(node *ast.ClosureNode) (reflect.Type, info) {
+ t, _ := v.visit(node.Node)
+ if t == nil {
+ return v.error(node.Node, "closure cannot be nil")
+ }
+ return reflect.FuncOf([]reflect.Type{anyType}, []reflect.Type{t}, false), info{}
+}
+
+func (v *checker) PointerNode(node *ast.PointerNode) (reflect.Type, info) {
+ if len(v.predicateScopes) == 0 {
+ return v.error(node, "cannot use pointer accessor outside closure")
+ }
+ scope := v.predicateScopes[len(v.predicateScopes)-1]
+ if node.Name == "" {
+ switch scope.vtype.Kind() {
+ case reflect.Interface:
+ return anyType, info{}
+ case reflect.Array, reflect.Slice:
+ return scope.vtype.Elem(), info{}
+ }
+ return v.error(node, "cannot use %v as array", scope)
+ }
+ if scope.vars != nil {
+ if t, ok := scope.vars[node.Name]; ok {
+ return t, info{}
+ }
+ }
+ return v.error(node, "unknown pointer #%v", node.Name)
+}
+
+func (v *checker) VariableDeclaratorNode(node *ast.VariableDeclaratorNode) (reflect.Type, info) {
+ if _, ok := v.config.Types[node.Name]; ok {
+ return v.error(node, "cannot redeclare %v", node.Name)
+ }
+ if _, ok := v.config.Functions[node.Name]; ok {
+ return v.error(node, "cannot redeclare function %v", node.Name)
+ }
+ if _, ok := v.config.Builtins[node.Name]; ok {
+ return v.error(node, "cannot redeclare builtin %v", node.Name)
+ }
+ if _, ok := v.lookupVariable(node.Name); ok {
+ return v.error(node, "cannot redeclare variable %v", node.Name)
+ }
+ vtype, vinfo := v.visit(node.Value)
+ v.varScopes = append(v.varScopes, varScope{node.Name, vtype, vinfo})
+ t, i := v.visit(node.Expr)
+ v.varScopes = v.varScopes[:len(v.varScopes)-1]
+ return t, i
+}
+
+func (v *checker) lookupVariable(name string) (varScope, bool) {
+ for i := len(v.varScopes) - 1; i >= 0; i-- {
+ if v.varScopes[i].name == name {
+ return v.varScopes[i], true
+ }
+ }
+ return varScope{}, false
+}
+
+func (v *checker) ConditionalNode(node *ast.ConditionalNode) (reflect.Type, info) {
+ c, _ := v.visit(node.Cond)
+ if !isBool(c) && !isAny(c) {
+ return v.error(node.Cond, "non-bool expression (type %v) used as condition", c)
+ }
+
+ t1, _ := v.visit(node.Exp1)
+ t2, _ := v.visit(node.Exp2)
+
+ if t1 == nil && t2 != nil {
+ return t2, info{}
+ }
+ if t1 != nil && t2 == nil {
+ return t1, info{}
+ }
+ if t1 == nil && t2 == nil {
+ return nilType, info{}
+ }
+ if t1.AssignableTo(t2) {
+ return t1, info{}
+ }
+ return anyType, info{}
+}
+
+func (v *checker) ArrayNode(node *ast.ArrayNode) (reflect.Type, info) {
+ var prev reflect.Type
+ allElementsAreSameType := true
+ for i, node := range node.Nodes {
+ curr, _ := v.visit(node)
+ if i > 0 {
+ if curr == nil || prev == nil {
+ allElementsAreSameType = false
+ } else if curr.Kind() != prev.Kind() {
+ allElementsAreSameType = false
+ }
+ }
+ prev = curr
+ }
+ if allElementsAreSameType && prev != nil {
+ return arrayType, info{elem: prev}
+ }
+ return arrayType, info{}
+}
+
+func (v *checker) MapNode(node *ast.MapNode) (reflect.Type, info) {
+ for _, pair := range node.Pairs {
+ v.visit(pair)
+ }
+ return mapType, info{}
+}
+
+func (v *checker) PairNode(node *ast.PairNode) (reflect.Type, info) {
+ v.visit(node.Key)
+ v.visit(node.Value)
+ return nilType, info{}
+}
diff --git a/vendor/github.com/expr-lang/expr/checker/info.go b/vendor/github.com/expr-lang/expr/checker/info.go
new file mode 100644
index 00000000..112bfab3
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/checker/info.go
@@ -0,0 +1,128 @@
+package checker
+
+import (
+ "reflect"
+
+ "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/conf"
+ "github.com/expr-lang/expr/vm"
+)
+
+func FieldIndex(types conf.TypesTable, node ast.Node) (bool, []int, string) {
+ switch n := node.(type) {
+ case *ast.IdentifierNode:
+ if t, ok := types[n.Value]; ok && len(t.FieldIndex) > 0 {
+ return true, t.FieldIndex, n.Value
+ }
+ case *ast.MemberNode:
+ base := n.Node.Type()
+ if kind(base) == reflect.Ptr {
+ base = base.Elem()
+ }
+ if kind(base) == reflect.Struct {
+ if prop, ok := n.Property.(*ast.StringNode); ok {
+ name := prop.Value
+ if field, ok := fetchField(base, name); ok {
+ return true, field.Index, name
+ }
+ }
+ }
+ }
+ return false, nil, ""
+}
+
+func MethodIndex(types conf.TypesTable, node ast.Node) (bool, int, string) {
+ switch n := node.(type) {
+ case *ast.IdentifierNode:
+ if t, ok := types[n.Value]; ok {
+ return t.Method, t.MethodIndex, n.Value
+ }
+ case *ast.MemberNode:
+ if name, ok := n.Property.(*ast.StringNode); ok {
+ base := n.Node.Type()
+ if base != nil && base.Kind() != reflect.Interface {
+ if m, ok := base.MethodByName(name.Value); ok {
+ return true, m.Index, name.Value
+ }
+ }
+ }
+ }
+ return false, 0, ""
+}
+
+func TypedFuncIndex(fn reflect.Type, method bool) (int, bool) {
+ if fn == nil {
+ return 0, false
+ }
+ if fn.Kind() != reflect.Func {
+ return 0, false
+ }
+ // OnCallTyped doesn't work for functions with variadic arguments.
+ if fn.IsVariadic() {
+ return 0, false
+ }
+ // OnCallTyped doesn't work named function, like `type MyFunc func() int`.
+ if fn.PkgPath() != "" { // If PkgPath() is not empty, it means that function is named.
+ return 0, false
+ }
+
+ fnNumIn := fn.NumIn()
+ fnInOffset := 0
+ if method {
+ fnNumIn--
+ fnInOffset = 1
+ }
+
+funcTypes:
+ for i := range vm.FuncTypes {
+ if i == 0 {
+ continue
+ }
+ typed := reflect.ValueOf(vm.FuncTypes[i]).Elem().Type()
+ if typed.Kind() != reflect.Func {
+ continue
+ }
+ if typed.NumOut() != fn.NumOut() {
+ continue
+ }
+ for j := 0; j < typed.NumOut(); j++ {
+ if typed.Out(j) != fn.Out(j) {
+ continue funcTypes
+ }
+ }
+ if typed.NumIn() != fnNumIn {
+ continue
+ }
+ for j := 0; j < typed.NumIn(); j++ {
+ if typed.In(j) != fn.In(j+fnInOffset) {
+ continue funcTypes
+ }
+ }
+ return i, true
+ }
+ return 0, false
+}
+
+func IsFastFunc(fn reflect.Type, method bool) bool {
+ if fn == nil {
+ return false
+ }
+ if fn.Kind() != reflect.Func {
+ return false
+ }
+ numIn := 1
+ if method {
+ numIn = 2
+ }
+ if !isAny(fn) &&
+ fn.IsVariadic() &&
+ fn.NumIn() == numIn &&
+ fn.NumOut() == 1 &&
+ fn.Out(0).Kind() == reflect.Interface {
+ rest := fn.In(fn.NumIn() - 1) // function has only one param for functions and two for methods
+ if kind(rest) == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/expr-lang/expr/checker/types.go b/vendor/github.com/expr-lang/expr/checker/types.go
new file mode 100644
index 00000000..d10736a7
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/checker/types.go
@@ -0,0 +1,228 @@
+package checker
+
+import (
+ "reflect"
+ "time"
+
+ "github.com/expr-lang/expr/conf"
+)
+
+var (
+ nilType = reflect.TypeOf(nil)
+ boolType = reflect.TypeOf(true)
+ integerType = reflect.TypeOf(0)
+ floatType = reflect.TypeOf(float64(0))
+ stringType = reflect.TypeOf("")
+ arrayType = reflect.TypeOf([]any{})
+ mapType = reflect.TypeOf(map[string]any{})
+ anyType = reflect.TypeOf(new(any)).Elem()
+ timeType = reflect.TypeOf(time.Time{})
+ durationType = reflect.TypeOf(time.Duration(0))
+)
+
+func combined(a, b reflect.Type) reflect.Type {
+ if a.Kind() == b.Kind() {
+ return a
+ }
+ if isFloat(a) || isFloat(b) {
+ return floatType
+ }
+ return integerType
+}
+
+func anyOf(t reflect.Type, fns ...func(reflect.Type) bool) bool {
+ for _, fn := range fns {
+ if fn(t) {
+ return true
+ }
+ }
+ return false
+}
+
+func or(l, r reflect.Type, fns ...func(reflect.Type) bool) bool {
+ if isAny(l) && isAny(r) {
+ return true
+ }
+ if isAny(l) && anyOf(r, fns...) {
+ return true
+ }
+ if isAny(r) && anyOf(l, fns...) {
+ return true
+ }
+ return false
+}
+
+func isAny(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Interface:
+ return true
+ }
+ }
+ return false
+}
+
+func isInteger(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ fallthrough
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return true
+ }
+ }
+ return false
+}
+
+func isFloat(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Float32, reflect.Float64:
+ return true
+ }
+ }
+ return false
+}
+
+func isNumber(t reflect.Type) bool {
+ return isInteger(t) || isFloat(t)
+}
+
+func isTime(t reflect.Type) bool {
+ if t != nil {
+ switch t {
+ case timeType:
+ return true
+ }
+ }
+ return false
+}
+
+func isDuration(t reflect.Type) bool {
+ if t != nil {
+ switch t {
+ case durationType:
+ return true
+ }
+ }
+ return false
+}
+
+func isBool(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Bool:
+ return true
+ }
+ }
+ return false
+}
+
+func isString(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.String:
+ return true
+ }
+ }
+ return false
+}
+
+func isArray(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Ptr:
+ return isArray(t.Elem())
+ case reflect.Slice, reflect.Array:
+ return true
+ }
+ }
+ return false
+}
+
+func isMap(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Ptr:
+ return isMap(t.Elem())
+ case reflect.Map:
+ return true
+ }
+ }
+ return false
+}
+
+func isStruct(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Ptr:
+ return isStruct(t.Elem())
+ case reflect.Struct:
+ return true
+ }
+ }
+ return false
+}
+
+func isFunc(t reflect.Type) bool {
+ if t != nil {
+ switch t.Kind() {
+ case reflect.Ptr:
+ return isFunc(t.Elem())
+ case reflect.Func:
+ return true
+ }
+ }
+ return false
+}
+
+func fetchField(t reflect.Type, name string) (reflect.StructField, bool) {
+ if t != nil {
+ // First check all structs fields.
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ // Search all fields, even embedded structs.
+ if conf.FieldName(field) == name {
+ return field, true
+ }
+ }
+
+ // Second check fields of embedded structs.
+ for i := 0; i < t.NumField(); i++ {
+ anon := t.Field(i)
+ if anon.Anonymous {
+ anonType := anon.Type
+ if anonType.Kind() == reflect.Pointer {
+ anonType = anonType.Elem()
+ }
+ if field, ok := fetchField(anonType, name); ok {
+ field.Index = append(anon.Index, field.Index...)
+ return field, true
+ }
+ }
+ }
+ }
+ return reflect.StructField{}, false
+}
+
+func kind(t reflect.Type) reflect.Kind {
+ if t == nil {
+ return reflect.Invalid
+ }
+ return t.Kind()
+}
+
+func isComparable(l, r reflect.Type) bool {
+ if l == nil || r == nil {
+ return true
+ }
+ switch {
+ case l.Kind() == r.Kind():
+ return true
+ case isNumber(l) && isNumber(r):
+ return true
+ case isAny(l) || isAny(r):
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/expr-lang/expr/compiler/compiler.go b/vendor/github.com/expr-lang/expr/compiler/compiler.go
new file mode 100644
index 00000000..720f6a26
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/compiler/compiler.go
@@ -0,0 +1,1223 @@
+package compiler
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "regexp"
+
+ "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/checker"
+ "github.com/expr-lang/expr/conf"
+ "github.com/expr-lang/expr/file"
+ "github.com/expr-lang/expr/parser"
+ . "github.com/expr-lang/expr/vm"
+ "github.com/expr-lang/expr/vm/runtime"
+)
+
+const (
+ placeholder = 12345
+)
+
+func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("%v", r)
+ }
+ }()
+
+ c := &compiler{
+ config: config,
+ locations: make([]file.Location, 0),
+ constantsIndex: make(map[any]int),
+ functionsIndex: make(map[string]int),
+ debugInfo: make(map[string]string),
+ }
+
+ c.compile(tree.Node)
+
+ if c.config != nil {
+ switch c.config.Expect {
+ case reflect.Int:
+ c.emit(OpCast, 0)
+ case reflect.Int64:
+ c.emit(OpCast, 1)
+ case reflect.Float64:
+ c.emit(OpCast, 2)
+ }
+ if c.config.Optimize {
+ c.optimize()
+ }
+ }
+
+ var span *Span
+ if len(c.spans) > 0 {
+ span = c.spans[0]
+ }
+
+ program = NewProgram(
+ tree.Source,
+ tree.Node,
+ c.locations,
+ c.variables,
+ c.constants,
+ c.bytecode,
+ c.arguments,
+ c.functions,
+ c.debugInfo,
+ span,
+ )
+ return
+}
+
+type compiler struct {
+ config *conf.Config
+ locations []file.Location
+ bytecode []Opcode
+ variables int
+ scopes []scope
+ constants []any
+ constantsIndex map[any]int
+ functions []Function
+ functionsIndex map[string]int
+ debugInfo map[string]string
+ nodes []ast.Node
+ spans []*Span
+ chains [][]int
+ arguments []int
+}
+
+type scope struct {
+ variableName string
+ index int
+}
+
+func (c *compiler) nodeParent() ast.Node {
+ if len(c.nodes) > 1 {
+ return c.nodes[len(c.nodes)-2]
+ }
+ return nil
+}
+
+func (c *compiler) emitLocation(loc file.Location, op Opcode, arg int) int {
+ c.bytecode = append(c.bytecode, op)
+ current := len(c.bytecode)
+ c.arguments = append(c.arguments, arg)
+ c.locations = append(c.locations, loc)
+ return current
+}
+
+func (c *compiler) emit(op Opcode, args ...int) int {
+ arg := 0
+ if len(args) > 1 {
+ panic("too many arguments")
+ }
+ if len(args) == 1 {
+ arg = args[0]
+ }
+ var loc file.Location
+ if len(c.nodes) > 0 {
+ loc = c.nodes[len(c.nodes)-1].Location()
+ }
+ return c.emitLocation(loc, op, arg)
+}
+
+func (c *compiler) emitPush(value any) int {
+ return c.emit(OpPush, c.addConstant(value))
+}
+
+func (c *compiler) addConstant(constant any) int {
+ indexable := true
+ hash := constant
+ switch reflect.TypeOf(constant).Kind() {
+ case reflect.Slice, reflect.Map, reflect.Struct, reflect.Func:
+ indexable = false
+ }
+ if field, ok := constant.(*runtime.Field); ok {
+ indexable = true
+ hash = fmt.Sprintf("%v", field)
+ }
+ if method, ok := constant.(*runtime.Method); ok {
+ indexable = true
+ hash = fmt.Sprintf("%v", method)
+ }
+ if indexable {
+ if p, ok := c.constantsIndex[hash]; ok {
+ return p
+ }
+ }
+ c.constants = append(c.constants, constant)
+ p := len(c.constants) - 1
+ if indexable {
+ c.constantsIndex[hash] = p
+ }
+ return p
+}
+
+func (c *compiler) addVariable(name string) int {
+ c.variables++
+ c.debugInfo[fmt.Sprintf("var_%d", c.variables-1)] = name
+ return c.variables - 1
+}
+
+// emitFunction adds builtin.Function.Func to the program.functions and emits call opcode.
+func (c *compiler) emitFunction(fn *builtin.Function, argsLen int) {
+ switch argsLen {
+ case 0:
+ c.emit(OpCall0, c.addFunction(fn.Name, fn.Func))
+ case 1:
+ c.emit(OpCall1, c.addFunction(fn.Name, fn.Func))
+ case 2:
+ c.emit(OpCall2, c.addFunction(fn.Name, fn.Func))
+ case 3:
+ c.emit(OpCall3, c.addFunction(fn.Name, fn.Func))
+ default:
+ c.emit(OpLoadFunc, c.addFunction(fn.Name, fn.Func))
+ c.emit(OpCallN, argsLen)
+ }
+}
+
+// addFunction adds builtin.Function.Func to the program.functions and returns its index.
+func (c *compiler) addFunction(name string, fn Function) int {
+ if fn == nil {
+ panic("function is nil")
+ }
+ if p, ok := c.functionsIndex[name]; ok {
+ return p
+ }
+ p := len(c.functions)
+ c.functions = append(c.functions, fn)
+ c.functionsIndex[name] = p
+ c.debugInfo[fmt.Sprintf("func_%d", p)] = name
+ return p
+}
+
+func (c *compiler) patchJump(placeholder int) {
+ offset := len(c.bytecode) - placeholder
+ c.arguments[placeholder-1] = offset
+}
+
+func (c *compiler) calcBackwardJump(to int) int {
+ return len(c.bytecode) + 1 - to
+}
+
+func (c *compiler) compile(node ast.Node) {
+ c.nodes = append(c.nodes, node)
+ defer func() {
+ c.nodes = c.nodes[:len(c.nodes)-1]
+ }()
+
+ if c.config != nil && c.config.Profile {
+ span := &Span{
+ Name: reflect.TypeOf(node).String(),
+ Expression: node.String(),
+ }
+ if len(c.spans) > 0 {
+ prev := c.spans[len(c.spans)-1]
+ prev.Children = append(prev.Children, span)
+ }
+ c.spans = append(c.spans, span)
+ defer func() {
+ if len(c.spans) > 1 {
+ c.spans = c.spans[:len(c.spans)-1]
+ }
+ }()
+
+ c.emit(OpProfileStart, c.addConstant(span))
+ defer func() {
+ c.emit(OpProfileEnd, c.addConstant(span))
+ }()
+ }
+
+ switch n := node.(type) {
+ case *ast.NilNode:
+ c.NilNode(n)
+ case *ast.IdentifierNode:
+ c.IdentifierNode(n)
+ case *ast.IntegerNode:
+ c.IntegerNode(n)
+ case *ast.FloatNode:
+ c.FloatNode(n)
+ case *ast.BoolNode:
+ c.BoolNode(n)
+ case *ast.StringNode:
+ c.StringNode(n)
+ case *ast.ConstantNode:
+ c.ConstantNode(n)
+ case *ast.UnaryNode:
+ c.UnaryNode(n)
+ case *ast.BinaryNode:
+ c.BinaryNode(n)
+ case *ast.ChainNode:
+ c.ChainNode(n)
+ case *ast.MemberNode:
+ c.MemberNode(n)
+ case *ast.SliceNode:
+ c.SliceNode(n)
+ case *ast.CallNode:
+ c.CallNode(n)
+ case *ast.BuiltinNode:
+ c.BuiltinNode(n)
+ case *ast.ClosureNode:
+ c.ClosureNode(n)
+ case *ast.PointerNode:
+ c.PointerNode(n)
+ case *ast.VariableDeclaratorNode:
+ c.VariableDeclaratorNode(n)
+ case *ast.ConditionalNode:
+ c.ConditionalNode(n)
+ case *ast.ArrayNode:
+ c.ArrayNode(n)
+ case *ast.MapNode:
+ c.MapNode(n)
+ case *ast.PairNode:
+ c.PairNode(n)
+ default:
+ panic(fmt.Sprintf("undefined node type (%T)", node))
+ }
+}
+
+func (c *compiler) NilNode(_ *ast.NilNode) {
+ c.emit(OpNil)
+}
+
+func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
+ if index, ok := c.lookupVariable(node.Value); ok {
+ c.emit(OpLoadVar, index)
+ return
+ }
+ if node.Value == "$env" {
+ c.emit(OpLoadEnv)
+ return
+ }
+
+ var mapEnv bool
+ var types conf.TypesTable
+ if c.config != nil {
+ mapEnv = c.config.MapEnv
+ types = c.config.Types
+ }
+
+ if mapEnv {
+ c.emit(OpLoadFast, c.addConstant(node.Value))
+ } else if ok, index, name := checker.FieldIndex(types, node); ok {
+ c.emit(OpLoadField, c.addConstant(&runtime.Field{
+ Index: index,
+ Path: []string{name},
+ }))
+ } else if ok, index, name := checker.MethodIndex(types, node); ok {
+ c.emit(OpLoadMethod, c.addConstant(&runtime.Method{
+ Name: name,
+ Index: index,
+ }))
+ } else {
+ c.emit(OpLoadConst, c.addConstant(node.Value))
+ }
+}
+
+func (c *compiler) IntegerNode(node *ast.IntegerNode) {
+ t := node.Type()
+ if t == nil {
+ c.emitPush(node.Value)
+ return
+ }
+ switch t.Kind() {
+ case reflect.Float32:
+ c.emitPush(float32(node.Value))
+ case reflect.Float64:
+ c.emitPush(float64(node.Value))
+ case reflect.Int:
+ c.emitPush(node.Value)
+ case reflect.Int8:
+ if node.Value > math.MaxInt8 || node.Value < math.MinInt8 {
+ panic(fmt.Sprintf("constant %d overflows int8", node.Value))
+ }
+ c.emitPush(int8(node.Value))
+ case reflect.Int16:
+ if node.Value > math.MaxInt16 || node.Value < math.MinInt16 {
+ panic(fmt.Sprintf("constant %d overflows int16", node.Value))
+ }
+ c.emitPush(int16(node.Value))
+ case reflect.Int32:
+ if node.Value > math.MaxInt32 || node.Value < math.MinInt32 {
+ panic(fmt.Sprintf("constant %d overflows int32", node.Value))
+ }
+ c.emitPush(int32(node.Value))
+ case reflect.Int64:
+ c.emitPush(int64(node.Value))
+ case reflect.Uint:
+ if node.Value < 0 {
+ panic(fmt.Sprintf("constant %d overflows uint", node.Value))
+ }
+ c.emitPush(uint(node.Value))
+ case reflect.Uint8:
+ if node.Value > math.MaxUint8 || node.Value < 0 {
+ panic(fmt.Sprintf("constant %d overflows uint8", node.Value))
+ }
+ c.emitPush(uint8(node.Value))
+ case reflect.Uint16:
+ if node.Value > math.MaxUint16 || node.Value < 0 {
+ panic(fmt.Sprintf("constant %d overflows uint16", node.Value))
+ }
+ c.emitPush(uint16(node.Value))
+ case reflect.Uint32:
+ if node.Value < 0 {
+ panic(fmt.Sprintf("constant %d overflows uint32", node.Value))
+ }
+ c.emitPush(uint32(node.Value))
+ case reflect.Uint64:
+ if node.Value < 0 {
+ panic(fmt.Sprintf("constant %d overflows uint64", node.Value))
+ }
+ c.emitPush(uint64(node.Value))
+ default:
+ c.emitPush(node.Value)
+ }
+}
+
+func (c *compiler) FloatNode(node *ast.FloatNode) {
+ t := node.Type()
+ if t == nil {
+ c.emitPush(node.Value)
+ return
+ }
+ switch t.Kind() {
+ case reflect.Float32:
+ c.emitPush(float32(node.Value))
+ case reflect.Float64:
+ c.emitPush(node.Value)
+ }
+}
+
+func (c *compiler) BoolNode(node *ast.BoolNode) {
+ if node.Value {
+ c.emit(OpTrue)
+ } else {
+ c.emit(OpFalse)
+ }
+}
+
+func (c *compiler) StringNode(node *ast.StringNode) {
+ c.emitPush(node.Value)
+}
+
+func (c *compiler) ConstantNode(node *ast.ConstantNode) {
+ c.emitPush(node.Value)
+}
+
+func (c *compiler) UnaryNode(node *ast.UnaryNode) {
+ c.compile(node.Node)
+ c.derefInNeeded(node.Node)
+
+ switch node.Operator {
+
+ case "!", "not":
+ c.emit(OpNot)
+
+ case "+":
+ // Do nothing
+
+ case "-":
+ c.emit(OpNegate)
+
+ default:
+ panic(fmt.Sprintf("unknown operator (%v)", node.Operator))
+ }
+}
+
+func (c *compiler) BinaryNode(node *ast.BinaryNode) {
+ switch node.Operator {
+ case "==":
+ c.equalBinaryNode(node)
+
+ case "!=":
+ c.equalBinaryNode(node)
+ c.emit(OpNot)
+
+ case "or", "||":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ end := c.emit(OpJumpIfTrue, placeholder)
+ c.emit(OpPop)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.patchJump(end)
+
+ case "and", "&&":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ end := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.patchJump(end)
+
+ case "<":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpLess)
+
+ case ">":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpMore)
+
+ case "<=":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpLessOrEqual)
+
+ case ">=":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpMoreOrEqual)
+
+ case "+":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpAdd)
+
+ case "-":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpSubtract)
+
+ case "*":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpMultiply)
+
+ case "/":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpDivide)
+
+ case "%":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpModulo)
+
+ case "**", "^":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpExponent)
+
+ case "in":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpIn)
+
+ case "matches":
+ if str, ok := node.Right.(*ast.StringNode); ok {
+ re, err := regexp.Compile(str.Value)
+ if err != nil {
+ panic(err)
+ }
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.emit(OpMatchesConst, c.addConstant(re))
+ } else {
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpMatches)
+ }
+
+ case "contains":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpContains)
+
+ case "startsWith":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpStartsWith)
+
+ case "endsWith":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpEndsWith)
+
+ case "..":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.emit(OpRange)
+
+ case "??":
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ end := c.emit(OpJumpIfNotNil, placeholder)
+ c.emit(OpPop)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+ c.patchJump(end)
+
+ default:
+ panic(fmt.Sprintf("unknown operator (%v)", node.Operator))
+
+ }
+}
+
+func (c *compiler) equalBinaryNode(node *ast.BinaryNode) {
+ l := kind(node.Left.Type())
+ r := kind(node.Right.Type())
+
+ leftIsSimple := isSimpleType(node.Left)
+ rightIsSimple := isSimpleType(node.Right)
+ leftAndRightAreSimple := leftIsSimple && rightIsSimple
+
+ c.compile(node.Left)
+ c.derefInNeeded(node.Left)
+ c.compile(node.Right)
+ c.derefInNeeded(node.Right)
+
+ if l == r && l == reflect.Int && leftAndRightAreSimple {
+ c.emit(OpEqualInt)
+ } else if l == r && l == reflect.String && leftAndRightAreSimple {
+ c.emit(OpEqualString)
+ } else {
+ c.emit(OpEqual)
+ }
+}
+
+func isSimpleType(node ast.Node) bool {
+ if node == nil {
+ return false
+ }
+ t := node.Type()
+ if t == nil {
+ return false
+ }
+ return t.PkgPath() == ""
+}
+
+func (c *compiler) ChainNode(node *ast.ChainNode) {
+ c.chains = append(c.chains, []int{})
+ c.compile(node.Node)
+ for _, ph := range c.chains[len(c.chains)-1] {
+ c.patchJump(ph) // If chain activated jump here (got nit somewhere).
+ }
+ parent := c.nodeParent()
+ if binary, ok := parent.(*ast.BinaryNode); ok && binary.Operator == "??" {
+ // If chain is used in nil coalescing operator, we can omit
+ // nil push at the end of the chain. The ?? operator will
+ // handle it.
+ } else {
+ // We need to put the nil on the stack, otherwise "typed"
+ // nil will be used as a result of the chain.
+ j := c.emit(OpJumpIfNotNil, placeholder)
+ c.emit(OpPop)
+ c.emit(OpNil)
+ c.patchJump(j)
+ }
+ c.chains = c.chains[:len(c.chains)-1]
+}
+
+func (c *compiler) MemberNode(node *ast.MemberNode) {
+ var types conf.TypesTable
+ if c.config != nil {
+ types = c.config.Types
+ }
+
+ if ok, index, name := checker.MethodIndex(types, node); ok {
+ c.compile(node.Node)
+ c.emit(OpMethod, c.addConstant(&runtime.Method{
+ Name: name,
+ Index: index,
+ }))
+ return
+ }
+ op := OpFetch
+ base := node.Node
+
+ ok, index, nodeName := checker.FieldIndex(types, node)
+ path := []string{nodeName}
+
+ if ok {
+ op = OpFetchField
+ for !node.Optional {
+ if ident, isIdent := base.(*ast.IdentifierNode); isIdent {
+ if ok, identIndex, name := checker.FieldIndex(types, ident); ok {
+ index = append(identIndex, index...)
+ path = append([]string{name}, path...)
+ c.emitLocation(ident.Location(), OpLoadField, c.addConstant(
+ &runtime.Field{Index: index, Path: path},
+ ))
+ return
+ }
+ }
+
+ if member, isMember := base.(*ast.MemberNode); isMember {
+ if ok, memberIndex, name := checker.FieldIndex(types, member); ok {
+ index = append(memberIndex, index...)
+ path = append([]string{name}, path...)
+ node = member
+ base = member.Node
+ } else {
+ break
+ }
+ } else {
+ break
+ }
+ }
+ }
+
+ c.compile(base)
+ if node.Optional {
+ ph := c.emit(OpJumpIfNil, placeholder)
+ c.chains[len(c.chains)-1] = append(c.chains[len(c.chains)-1], ph)
+ }
+
+ if op == OpFetch {
+ c.compile(node.Property)
+ c.emit(OpFetch)
+ } else {
+ c.emitLocation(node.Location(), op, c.addConstant(
+ &runtime.Field{Index: index, Path: path},
+ ))
+ }
+}
+
+func (c *compiler) SliceNode(node *ast.SliceNode) {
+ c.compile(node.Node)
+ if node.To != nil {
+ c.compile(node.To)
+ } else {
+ c.emit(OpLen)
+ }
+ if node.From != nil {
+ c.compile(node.From)
+ } else {
+ c.emitPush(0)
+ }
+ c.emit(OpSlice)
+}
+
+func (c *compiler) CallNode(node *ast.CallNode) {
+ fn := node.Callee.Type()
+ if kind(fn) == reflect.Func {
+ fnInOffset := 0
+ fnNumIn := fn.NumIn()
+ switch callee := node.Callee.(type) {
+ case *ast.MemberNode:
+ if prop, ok := callee.Property.(*ast.StringNode); ok {
+ if _, ok = callee.Node.Type().MethodByName(prop.Value); ok && callee.Node.Type().Kind() != reflect.Interface {
+ fnInOffset = 1
+ fnNumIn--
+ }
+ }
+ case *ast.IdentifierNode:
+ if t, ok := c.config.Types[callee.Value]; ok && t.Method {
+ fnInOffset = 1
+ fnNumIn--
+ }
+ }
+ for i, arg := range node.Arguments {
+ c.compile(arg)
+ if k := kind(arg.Type()); k == reflect.Ptr || k == reflect.Interface {
+ var in reflect.Type
+ if fn.IsVariadic() && i >= fnNumIn-1 {
+ in = fn.In(fn.NumIn() - 1).Elem()
+ } else {
+ in = fn.In(i + fnInOffset)
+ }
+ if k = kind(in); k != reflect.Ptr && k != reflect.Interface {
+ c.emit(OpDeref)
+ }
+ }
+ }
+ } else {
+ for _, arg := range node.Arguments {
+ c.compile(arg)
+ }
+ }
+
+ if ident, ok := node.Callee.(*ast.IdentifierNode); ok {
+ if c.config != nil {
+ if fn, ok := c.config.Functions[ident.Value]; ok {
+ c.emitFunction(fn, len(node.Arguments))
+ return
+ }
+ }
+ }
+ c.compile(node.Callee)
+
+ isMethod, _, _ := checker.MethodIndex(c.config.Types, node.Callee)
+ if index, ok := checker.TypedFuncIndex(node.Callee.Type(), isMethod); ok {
+ c.emit(OpCallTyped, index)
+ return
+ } else if checker.IsFastFunc(node.Callee.Type(), isMethod) {
+ c.emit(OpCallFast, len(node.Arguments))
+ } else {
+ c.emit(OpCall, len(node.Arguments))
+ }
+}
+
+func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
+ switch node.Name {
+ case "all":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ loopBreak = c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ })
+ c.emit(OpTrue)
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "none":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emit(OpNot)
+ loopBreak = c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ })
+ c.emit(OpTrue)
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "any":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ loopBreak = c.emit(OpJumpIfTrue, placeholder)
+ c.emit(OpPop)
+ })
+ c.emit(OpFalse)
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "one":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emitCond(func() {
+ c.emit(OpIncrementCount)
+ })
+ })
+ c.emit(OpGetCount)
+ c.emitPush(1)
+ c.emit(OpEqual)
+ c.emit(OpEnd)
+ return
+
+ case "filter":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emitCond(func() {
+ c.emit(OpIncrementCount)
+ if node.Map != nil {
+ c.compile(node.Map)
+ } else {
+ c.emit(OpPointer)
+ }
+ })
+ })
+ c.emit(OpGetCount)
+ c.emit(OpEnd)
+ c.emit(OpArray)
+ return
+
+ case "map":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ })
+ c.emit(OpGetLen)
+ c.emit(OpEnd)
+ c.emit(OpArray)
+ return
+
+ case "count":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ c.emitLoop(func() {
+ if len(node.Arguments) == 2 {
+ c.compile(node.Arguments[1])
+ } else {
+ c.emit(OpPointer)
+ }
+ c.emitCond(func() {
+ c.emit(OpIncrementCount)
+ })
+ })
+ c.emit(OpGetCount)
+ c.emit(OpEnd)
+ return
+
+ case "sum":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ c.emit(OpInt, 0)
+ c.emit(OpSetAcc)
+ c.emitLoop(func() {
+ if len(node.Arguments) == 2 {
+ c.compile(node.Arguments[1])
+ } else {
+ c.emit(OpPointer)
+ }
+ c.emit(OpGetAcc)
+ c.emit(OpAdd)
+ c.emit(OpSetAcc)
+ })
+ c.emit(OpGetAcc)
+ c.emit(OpEnd)
+ return
+
+ case "find":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ if node.Map != nil {
+ c.compile(node.Map)
+ } else {
+ c.emit(OpPointer)
+ }
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ if node.Throws {
+ c.emit(OpPush, c.addConstant(fmt.Errorf("reflect: slice index out of range")))
+ c.emit(OpThrow)
+ } else {
+ c.emit(OpNil)
+ }
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "findIndex":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ c.emit(OpGetIndex)
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ c.emit(OpNil)
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "findLast":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoopBackwards(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ if node.Map != nil {
+ c.compile(node.Map)
+ } else {
+ c.emit(OpPointer)
+ }
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ if node.Throws {
+ c.emit(OpPush, c.addConstant(fmt.Errorf("reflect: slice index out of range")))
+ c.emit(OpThrow)
+ } else {
+ c.emit(OpNil)
+ }
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "findLastIndex":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoopBackwards(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ c.emit(OpGetIndex)
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ c.emit(OpNil)
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "groupBy":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ c.emit(OpCreate, 1)
+ c.emit(OpSetAcc)
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emit(OpGroupBy)
+ })
+ c.emit(OpGetAcc)
+ c.emit(OpEnd)
+ return
+
+ case "sortBy":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ if len(node.Arguments) == 3 {
+ c.compile(node.Arguments[2])
+ } else {
+ c.emit(OpPush, c.addConstant("asc"))
+ }
+ c.emit(OpCreate, 2)
+ c.emit(OpSetAcc)
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emit(OpSortBy)
+ })
+ c.emit(OpSort)
+ c.emit(OpEnd)
+ return
+
+ case "reduce":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ if len(node.Arguments) == 3 {
+ c.compile(node.Arguments[2])
+ c.emit(OpSetAcc)
+ } else {
+ c.emit(OpPointer)
+ c.emit(OpIncrementIndex)
+ c.emit(OpSetAcc)
+ }
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emit(OpSetAcc)
+ })
+ c.emit(OpGetAcc)
+ c.emit(OpEnd)
+ return
+
+ }
+
+ if id, ok := builtin.Index[node.Name]; ok {
+ f := builtin.Builtins[id]
+ for _, arg := range node.Arguments {
+ c.compile(arg)
+ }
+
+ if f.Fast != nil {
+ c.emit(OpCallBuiltin1, id)
+ } else if f.Safe != nil {
+ c.emit(OpPush, c.addConstant(f.Safe))
+ c.emit(OpCallSafe, len(node.Arguments))
+ } else if f.Func != nil {
+ c.emitFunction(f, len(node.Arguments))
+ }
+ return
+ }
+
+ panic(fmt.Sprintf("unknown builtin %v", node.Name))
+}
+
+func (c *compiler) emitCond(body func()) {
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+
+ body()
+
+ jmp := c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ c.patchJump(jmp)
+}
+
+func (c *compiler) emitLoop(body func()) {
+ begin := len(c.bytecode)
+ end := c.emit(OpJumpIfEnd, placeholder)
+
+ body()
+
+ c.emit(OpIncrementIndex)
+ c.emit(OpJumpBackward, c.calcBackwardJump(begin))
+ c.patchJump(end)
+}
+
+func (c *compiler) emitLoopBackwards(body func()) {
+ c.emit(OpGetLen)
+ c.emit(OpInt, 1)
+ c.emit(OpSubtract)
+ c.emit(OpSetIndex)
+ begin := len(c.bytecode)
+ c.emit(OpGetIndex)
+ c.emit(OpInt, 0)
+ c.emit(OpMoreOrEqual)
+ end := c.emit(OpJumpIfFalse, placeholder)
+
+ body()
+
+ c.emit(OpDecrementIndex)
+ c.emit(OpJumpBackward, c.calcBackwardJump(begin))
+ c.patchJump(end)
+}
+
+func (c *compiler) ClosureNode(node *ast.ClosureNode) {
+ c.compile(node.Node)
+}
+
+func (c *compiler) PointerNode(node *ast.PointerNode) {
+ switch node.Name {
+ case "index":
+ c.emit(OpGetIndex)
+ case "acc":
+ c.emit(OpGetAcc)
+ case "":
+ c.emit(OpPointer)
+ default:
+ panic(fmt.Sprintf("unknown pointer %v", node.Name))
+ }
+}
+
+func (c *compiler) VariableDeclaratorNode(node *ast.VariableDeclaratorNode) {
+ c.compile(node.Value)
+ index := c.addVariable(node.Name)
+ c.emit(OpStore, index)
+ c.beginScope(node.Name, index)
+ c.compile(node.Expr)
+ c.endScope()
+}
+
+func (c *compiler) beginScope(name string, index int) {
+ c.scopes = append(c.scopes, scope{name, index})
+}
+
+func (c *compiler) endScope() {
+ c.scopes = c.scopes[:len(c.scopes)-1]
+}
+
+func (c *compiler) lookupVariable(name string) (int, bool) {
+ for i := len(c.scopes) - 1; i >= 0; i-- {
+ if c.scopes[i].variableName == name {
+ return c.scopes[i].index, true
+ }
+ }
+ return 0, false
+}
+
+func (c *compiler) ConditionalNode(node *ast.ConditionalNode) {
+ c.compile(node.Cond)
+ otherwise := c.emit(OpJumpIfFalse, placeholder)
+
+ c.emit(OpPop)
+ c.compile(node.Exp1)
+ end := c.emit(OpJump, placeholder)
+
+ c.patchJump(otherwise)
+ c.emit(OpPop)
+ c.compile(node.Exp2)
+
+ c.patchJump(end)
+}
+
+func (c *compiler) ArrayNode(node *ast.ArrayNode) {
+ for _, node := range node.Nodes {
+ c.compile(node)
+ }
+
+ c.emitPush(len(node.Nodes))
+ c.emit(OpArray)
+}
+
+func (c *compiler) MapNode(node *ast.MapNode) {
+ for _, pair := range node.Pairs {
+ c.compile(pair)
+ }
+
+ c.emitPush(len(node.Pairs))
+ c.emit(OpMap)
+}
+
+func (c *compiler) PairNode(node *ast.PairNode) {
+ c.compile(node.Key)
+ c.compile(node.Value)
+}
+
+func (c *compiler) derefInNeeded(node ast.Node) {
+ switch kind(node.Type()) {
+ case reflect.Ptr, reflect.Interface:
+ c.emit(OpDeref)
+ }
+}
+
+func (c *compiler) optimize() {
+ for i, op := range c.bytecode {
+ switch op {
+ case OpJumpIfTrue, OpJumpIfFalse, OpJumpIfNil, OpJumpIfNotNil:
+ target := i + c.arguments[i] + 1
+ for target < len(c.bytecode) && c.bytecode[target] == op {
+ target += c.arguments[target] + 1
+ }
+ c.arguments[i] = target - i - 1
+ }
+ }
+}
+
+func kind(t reflect.Type) reflect.Kind {
+ if t == nil {
+ return reflect.Invalid
+ }
+ return t.Kind()
+}
diff --git a/vendor/github.com/expr-lang/expr/conf/config.go b/vendor/github.com/expr-lang/expr/conf/config.go
new file mode 100644
index 00000000..01a407a1
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/conf/config.go
@@ -0,0 +1,106 @@
+package conf
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/vm/runtime"
+)
+
+type FunctionsTable map[string]*builtin.Function
+
+type Config struct {
+ Env any
+ Types TypesTable
+ MapEnv bool
+ DefaultType reflect.Type
+ Expect reflect.Kind
+ ExpectAny bool
+ Optimize bool
+ Strict bool
+ Profile bool
+ ConstFns map[string]reflect.Value
+ Visitors []ast.Visitor
+ Functions FunctionsTable
+ Builtins FunctionsTable
+ Disabled map[string]bool // disabled builtins
+}
+
+// CreateNew creates new config with default values.
+func CreateNew() *Config {
+ c := &Config{
+ Optimize: true,
+ Types: make(TypesTable),
+ ConstFns: make(map[string]reflect.Value),
+ Functions: make(map[string]*builtin.Function),
+ Builtins: make(map[string]*builtin.Function),
+ Disabled: make(map[string]bool),
+ }
+ for _, f := range builtin.Builtins {
+ c.Builtins[f.Name] = f
+ }
+ return c
+}
+
+// New creates new config with environment.
+func New(env any) *Config {
+ c := CreateNew()
+ c.WithEnv(env)
+ return c
+}
+
+func (c *Config) WithEnv(env any) {
+ var mapEnv bool
+ var mapValueType reflect.Type
+ if _, ok := env.(map[string]any); ok {
+ mapEnv = true
+ } else {
+ if reflect.ValueOf(env).Kind() == reflect.Map {
+ mapValueType = reflect.TypeOf(env).Elem()
+ }
+ }
+
+ c.Env = env
+ types := CreateTypesTable(env)
+ for name, t := range types {
+ c.Types[name] = t
+ }
+ c.MapEnv = mapEnv
+ c.DefaultType = mapValueType
+ c.Strict = true
+}
+
+func (c *Config) ConstExpr(name string) {
+ if c.Env == nil {
+ panic("no environment is specified for ConstExpr()")
+ }
+ fn := reflect.ValueOf(runtime.Fetch(c.Env, name))
+ if fn.Kind() != reflect.Func {
+ panic(fmt.Errorf("const expression %q must be a function", name))
+ }
+ c.ConstFns[name] = fn
+}
+
+type Checker interface {
+ Check()
+}
+
+func (c *Config) Check() {
+ for _, v := range c.Visitors {
+ if c, ok := v.(Checker); ok {
+ c.Check()
+ }
+ }
+}
+
+func (c *Config) IsOverridden(name string) bool {
+ if _, ok := c.Functions[name]; ok {
+ return true
+ }
+ if _, ok := c.Types[name]; ok {
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/expr-lang/expr/conf/types_table.go b/vendor/github.com/expr-lang/expr/conf/types_table.go
new file mode 100644
index 00000000..a42a4287
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/conf/types_table.go
@@ -0,0 +1,121 @@
+package conf
+
+import (
+ "reflect"
+
+ "github.com/expr-lang/expr/internal/deref"
+)
+
+type TypesTable map[string]Tag
+
+type Tag struct {
+ Type reflect.Type
+ Ambiguous bool
+ FieldIndex []int
+ Method bool
+ MethodIndex int
+}
+
+// CreateTypesTable creates types table for type checks during parsing.
+// If struct is passed, all fields will be treated as variables,
+// as well as all fields of embedded structs and struct itself.
+//
+// If map is passed, all items will be treated as variables
+// (key as name, value as type).
+func CreateTypesTable(i any) TypesTable {
+ if i == nil {
+ return nil
+ }
+
+ types := make(TypesTable)
+ v := reflect.ValueOf(i)
+ t := reflect.TypeOf(i)
+
+ d := t
+ if t.Kind() == reflect.Ptr {
+ d = t.Elem()
+ }
+
+ switch d.Kind() {
+ case reflect.Struct:
+ types = FieldsFromStruct(d)
+
+ // Methods of struct should be gathered from original struct with pointer,
+ // as methods maybe declared on pointer receiver. Also this method retrieves
+ // all embedded structs methods as well, no need to recursion.
+ for i := 0; i < t.NumMethod(); i++ {
+ m := t.Method(i)
+ types[m.Name] = Tag{
+ Type: m.Type,
+ Method: true,
+ MethodIndex: i,
+ }
+ }
+
+ case reflect.Map:
+ for _, key := range v.MapKeys() {
+ value := v.MapIndex(key)
+ if key.Kind() == reflect.String && value.IsValid() && value.CanInterface() {
+ if key.String() == "$env" { // Could check for all keywords here
+ panic("attempt to misuse env keyword as env map key")
+ }
+ types[key.String()] = Tag{Type: reflect.TypeOf(value.Interface())}
+ }
+ }
+
+ // A map may have method too.
+ for i := 0; i < t.NumMethod(); i++ {
+ m := t.Method(i)
+ types[m.Name] = Tag{
+ Type: m.Type,
+ Method: true,
+ MethodIndex: i,
+ }
+ }
+ }
+
+ return types
+}
+
+func FieldsFromStruct(t reflect.Type) TypesTable {
+ types := make(TypesTable)
+ t = deref.Type(t)
+ if t == nil {
+ return types
+ }
+
+ switch t.Kind() {
+ case reflect.Struct:
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+
+ if f.Anonymous {
+ for name, typ := range FieldsFromStruct(f.Type) {
+ if _, ok := types[name]; ok {
+ types[name] = Tag{Ambiguous: true}
+ } else {
+ typ.FieldIndex = append(f.Index, typ.FieldIndex...)
+ types[name] = typ
+ }
+ }
+ }
+ if fn := FieldName(f); fn == "$env" { // Could check for all keywords here
+ panic("attempt to misuse env keyword as env struct field tag")
+ } else {
+ types[FieldName(f)] = Tag{
+ Type: f.Type,
+ FieldIndex: f.Index,
+ }
+ }
+ }
+ }
+
+ return types
+}
+
+func FieldName(field reflect.StructField) string {
+ if taggedName := field.Tag.Get("expr"); taggedName != "" {
+ return taggedName
+ }
+ return field.Name
+}
diff --git a/vendor/github.com/expr-lang/expr/expr.go b/vendor/github.com/expr-lang/expr/expr.go
new file mode 100644
index 00000000..8c619e1c
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/expr.go
@@ -0,0 +1,254 @@
+package expr
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "time"
+
+ "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/checker"
+ "github.com/expr-lang/expr/compiler"
+ "github.com/expr-lang/expr/conf"
+ "github.com/expr-lang/expr/file"
+ "github.com/expr-lang/expr/optimizer"
+ "github.com/expr-lang/expr/patcher"
+ "github.com/expr-lang/expr/vm"
+)
+
+// Option for configuring config.
+type Option func(c *conf.Config)
+
+// Env specifies expected input of env for type checks.
+// If struct is passed, all fields will be treated as variables,
+// as well as all fields of embedded structs and struct itself.
+// If map is passed, all items will be treated as variables.
+// Methods defined on this type will be available as functions.
+func Env(env any) Option {
+ return func(c *conf.Config) {
+ c.WithEnv(env)
+ }
+}
+
+// AllowUndefinedVariables allows to use undefined variables inside expressions.
+// This can be used with expr.Env option to partially define a few variables.
+func AllowUndefinedVariables() Option {
+ return func(c *conf.Config) {
+ c.Strict = false
+ }
+}
+
+// Operator allows to replace a binary operator with a function.
+func Operator(operator string, fn ...string) Option {
+ return func(c *conf.Config) {
+ p := &patcher.OperatorOverloading{
+ Operator: operator,
+ Overloads: fn,
+ Types: c.Types,
+ Functions: c.Functions,
+ }
+ c.Visitors = append(c.Visitors, p)
+ }
+}
+
+// ConstExpr defines func expression as constant. If all argument to this function is constants,
+// then it can be replaced by result of this func call on compile step.
+func ConstExpr(fn string) Option {
+ return func(c *conf.Config) {
+ c.ConstExpr(fn)
+ }
+}
+
+// AsAny tells the compiler to expect any result.
+func AsAny() Option {
+ return func(c *conf.Config) {
+ c.ExpectAny = true
+ }
+}
+
+// AsKind tells the compiler to expect kind of the result.
+func AsKind(kind reflect.Kind) Option {
+ return func(c *conf.Config) {
+ c.Expect = kind
+ c.ExpectAny = true
+ }
+}
+
+// AsBool tells the compiler to expect a boolean result.
+func AsBool() Option {
+ return func(c *conf.Config) {
+ c.Expect = reflect.Bool
+ c.ExpectAny = true
+ }
+}
+
+// AsInt tells the compiler to expect an int result.
+func AsInt() Option {
+ return func(c *conf.Config) {
+ c.Expect = reflect.Int
+ c.ExpectAny = true
+ }
+}
+
+// AsInt64 tells the compiler to expect an int64 result.
+func AsInt64() Option {
+ return func(c *conf.Config) {
+ c.Expect = reflect.Int64
+ c.ExpectAny = true
+ }
+}
+
+// AsFloat64 tells the compiler to expect a float64 result.
+func AsFloat64() Option {
+ return func(c *conf.Config) {
+ c.Expect = reflect.Float64
+ c.ExpectAny = true
+ }
+}
+
+// WarnOnAny tells the compiler to warn if expression return any type.
+func WarnOnAny() Option {
+ return func(c *conf.Config) {
+ if c.Expect == reflect.Invalid {
+ panic("WarnOnAny() works only with combination with AsInt(), AsBool(), etc. options")
+ }
+ c.ExpectAny = false
+ }
+}
+
+// Optimize turns optimizations on or off.
+func Optimize(b bool) Option {
+ return func(c *conf.Config) {
+ c.Optimize = b
+ }
+}
+
+// Patch adds visitor to list of visitors what will be applied before compiling AST to bytecode.
+func Patch(visitor ast.Visitor) Option {
+ return func(c *conf.Config) {
+ c.Visitors = append(c.Visitors, visitor)
+ }
+}
+
+// Function adds function to list of functions what will be available in expressions.
+func Function(name string, fn func(params ...any) (any, error), types ...any) Option {
+ return func(c *conf.Config) {
+ ts := make([]reflect.Type, len(types))
+ for i, t := range types {
+ t := reflect.TypeOf(t)
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() != reflect.Func {
+ panic(fmt.Sprintf("expr: type of %s is not a function", name))
+ }
+ ts[i] = t
+ }
+ c.Functions[name] = &builtin.Function{
+ Name: name,
+ Func: fn,
+ Types: ts,
+ }
+ }
+}
+
+// DisableAllBuiltins disables all builtins.
+func DisableAllBuiltins() Option {
+ return func(c *conf.Config) {
+ for name := range c.Builtins {
+ c.Disabled[name] = true
+ }
+ }
+}
+
+// DisableBuiltin disables builtin function.
+func DisableBuiltin(name string) Option {
+ return func(c *conf.Config) {
+ c.Disabled[name] = true
+ }
+}
+
+// EnableBuiltin enables builtin function.
+func EnableBuiltin(name string) Option {
+ return func(c *conf.Config) {
+ delete(c.Disabled, name)
+ }
+}
+
+// WithContext passes context to all functions calls with a context.Context argument.
+func WithContext(name string) Option {
+ return Patch(patcher.WithContext{
+ Name: name,
+ })
+}
+
+// Timezone sets default timezone for date() and now() builtin functions.
+func Timezone(name string) Option {
+ tz, err := time.LoadLocation(name)
+ if err != nil {
+ panic(err)
+ }
+ return Patch(patcher.WithTimezone{
+ Location: tz,
+ })
+}
+
+// Compile parses and compiles given input expression to bytecode program.
+func Compile(input string, ops ...Option) (*vm.Program, error) {
+ config := conf.CreateNew()
+ for _, op := range ops {
+ op(config)
+ }
+ for name := range config.Disabled {
+ delete(config.Builtins, name)
+ }
+ config.Check()
+
+ tree, err := checker.ParseCheck(input, config)
+ if err != nil {
+ return nil, err
+ }
+
+ if config.Optimize {
+ err = optimizer.Optimize(&tree.Node, config)
+ if err != nil {
+ var fileError *file.Error
+ if errors.As(err, &fileError) {
+ return nil, fileError.Bind(tree.Source)
+ }
+ return nil, err
+ }
+ }
+
+ program, err := compiler.Compile(tree, config)
+ if err != nil {
+ return nil, err
+ }
+
+ return program, nil
+}
+
+// Run evaluates given bytecode program.
+func Run(program *vm.Program, env any) (any, error) {
+ return vm.Run(program, env)
+}
+
+// Eval parses, compiles and runs given input.
+func Eval(input string, env any) (any, error) {
+ if _, ok := env.(Option); ok {
+ return nil, fmt.Errorf("misused expr.Eval: second argument (env) should be passed without expr.Env")
+ }
+
+ program, err := Compile(input)
+ if err != nil {
+ return nil, err
+ }
+
+ output, err := Run(program, env)
+ if err != nil {
+ return nil, err
+ }
+
+ return output, nil
+}
diff --git a/vendor/github.com/expr-lang/expr/file/error.go b/vendor/github.com/expr-lang/expr/file/error.go
new file mode 100644
index 00000000..8ff85dfa
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/file/error.go
@@ -0,0 +1,81 @@
+package file
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+)
+
+type Error struct {
+ Location
+ Line int `json:"line"`
+ Column int `json:"column"`
+ Message string `json:"message"`
+ Snippet string `json:"snippet"`
+ Prev error `json:"prev"`
+}
+
+func (e *Error) Error() string {
+ return e.format()
+}
+
+func (e *Error) Bind(source Source) *Error {
+ e.Line = 1
+ for i, r := range source {
+ if i == e.From {
+ break
+ }
+ if r == '\n' {
+ e.Line++
+ e.Column = 0
+ } else {
+ e.Column++
+ }
+ }
+ if snippet, found := source.Snippet(e.Line); found {
+ snippet := strings.Replace(snippet, "\t", " ", -1)
+ srcLine := "\n | " + snippet
+ var bytes = []byte(snippet)
+ var indLine = "\n | "
+ for i := 0; i < e.Column && len(bytes) > 0; i++ {
+ _, sz := utf8.DecodeRune(bytes)
+ bytes = bytes[sz:]
+ if sz > 1 {
+ goto noind
+ } else {
+ indLine += "."
+ }
+ }
+ if _, sz := utf8.DecodeRune(bytes); sz > 1 {
+ goto noind
+ } else {
+ indLine += "^"
+ }
+ srcLine += indLine
+
+ noind:
+ e.Snippet = srcLine
+ }
+ return e
+}
+
+func (e *Error) Unwrap() error {
+ return e.Prev
+}
+
+func (e *Error) Wrap(err error) {
+ e.Prev = err
+}
+
+func (e *Error) format() string {
+ if e.Snippet == "" {
+ return e.Message
+ }
+ return fmt.Sprintf(
+ "%s (%d:%d)%s",
+ e.Message,
+ e.Line,
+ e.Column+1, // add one to the 0-based column for display
+ e.Snippet,
+ )
+}
diff --git a/vendor/github.com/expr-lang/expr/file/location.go b/vendor/github.com/expr-lang/expr/file/location.go
new file mode 100644
index 00000000..6c6bc242
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/file/location.go
@@ -0,0 +1,6 @@
+package file
+
+type Location struct {
+ From int `json:"from"`
+ To int `json:"to"`
+}
diff --git a/vendor/github.com/expr-lang/expr/file/source.go b/vendor/github.com/expr-lang/expr/file/source.go
new file mode 100644
index 00000000..8e2b2d15
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/file/source.go
@@ -0,0 +1,48 @@
+package file
+
+import (
+ "strings"
+ "unicode/utf8"
+)
+
+type Source []rune
+
+func NewSource(contents string) Source {
+ return []rune(contents)
+}
+
+func (s Source) String() string {
+ return string(s)
+}
+
+func (s Source) Snippet(line int) (string, bool) {
+ if s == nil {
+ return "", false
+ }
+ lines := strings.Split(string(s), "\n")
+ lineOffsets := make([]int, len(lines))
+ var offset int
+ for i, line := range lines {
+ offset = offset + utf8.RuneCountInString(line) + 1
+ lineOffsets[i] = offset
+ }
+ charStart, found := getLineOffset(lineOffsets, line)
+ if !found || len(s) == 0 {
+ return "", false
+ }
+ charEnd, found := getLineOffset(lineOffsets, line+1)
+ if found {
+ return string(s[charStart : charEnd-1]), true
+ }
+ return string(s[charStart:]), true
+}
+
+func getLineOffset(lineOffsets []int, line int) (int, bool) {
+ if line == 1 {
+ return 0, true
+ } else if line > 1 && line <= len(lineOffsets) {
+ offset := lineOffsets[line-2]
+ return offset, true
+ }
+ return -1, false
+}
diff --git a/vendor/github.com/expr-lang/expr/internal/deref/deref.go b/vendor/github.com/expr-lang/expr/internal/deref/deref.go
new file mode 100644
index 00000000..acdc8981
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/internal/deref/deref.go
@@ -0,0 +1,47 @@
+package deref
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func Deref(p any) any {
+ if p == nil {
+ return nil
+ }
+
+ v := reflect.ValueOf(p)
+
+ for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
+ if v.IsNil() {
+ return nil
+ }
+ v = v.Elem()
+ }
+
+ if v.IsValid() {
+ return v.Interface()
+ }
+
+ panic(fmt.Sprintf("cannot dereference %v", p))
+}
+
+func Type(t reflect.Type) reflect.Type {
+ if t == nil {
+ return nil
+ }
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ return t
+}
+
+func Value(v reflect.Value) reflect.Value {
+ for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
+ if v.IsNil() {
+ return v
+ }
+ v = v.Elem()
+ }
+ return v
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/const_expr.go b/vendor/github.com/expr-lang/expr/optimizer/const_expr.go
new file mode 100644
index 00000000..501ea3c5
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/const_expr.go
@@ -0,0 +1,85 @@
+package optimizer
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ . "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/file"
+)
+
+var errorType = reflect.TypeOf((*error)(nil)).Elem()
+
+type constExpr struct {
+ applied bool
+ err error
+ fns map[string]reflect.Value
+}
+
+func (c *constExpr) Visit(node *Node) {
+ defer func() {
+ if r := recover(); r != nil {
+ msg := fmt.Sprintf("%v", r)
+ // Make message more actual, it's a runtime error, but at compile step.
+ msg = strings.Replace(msg, "runtime error:", "compile error:", 1)
+ c.err = &file.Error{
+ Location: (*node).Location(),
+ Message: msg,
+ }
+ }
+ }()
+
+ patch := func(newNode Node) {
+ c.applied = true
+ Patch(node, newNode)
+ }
+
+ if call, ok := (*node).(*CallNode); ok {
+ if name, ok := call.Callee.(*IdentifierNode); ok {
+ fn, ok := c.fns[name.Value]
+ if ok {
+ in := make([]reflect.Value, len(call.Arguments))
+ for i := 0; i < len(call.Arguments); i++ {
+ arg := call.Arguments[i]
+ var param any
+
+ switch a := arg.(type) {
+ case *NilNode:
+ param = nil
+ case *IntegerNode:
+ param = a.Value
+ case *FloatNode:
+ param = a.Value
+ case *BoolNode:
+ param = a.Value
+ case *StringNode:
+ param = a.Value
+ case *ConstantNode:
+ param = a.Value
+
+ default:
+ return // Const expr optimization not applicable.
+ }
+
+ if param == nil && reflect.TypeOf(param) == nil {
+ // In case of nil value and nil type use this hack,
+ // otherwise reflect.Call will panic on zero value.
+ in[i] = reflect.ValueOf(¶m).Elem()
+ } else {
+ in[i] = reflect.ValueOf(param)
+ }
+ }
+
+ out := fn.Call(in)
+ value := out[0].Interface()
+ if len(out) == 2 && out[1].Type() == errorType && !out[1].IsNil() {
+ c.err = out[1].Interface().(error)
+ return
+ }
+ constNode := &ConstantNode{Value: value}
+ patch(constNode)
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/filter_first.go b/vendor/github.com/expr-lang/expr/optimizer/filter_first.go
new file mode 100644
index 00000000..7ea8f6fa
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/filter_first.go
@@ -0,0 +1,38 @@
+package optimizer
+
+import (
+ . "github.com/expr-lang/expr/ast"
+)
+
+type filterFirst struct{}
+
+func (*filterFirst) Visit(node *Node) {
+ if member, ok := (*node).(*MemberNode); ok && member.Property != nil && !member.Optional {
+ if prop, ok := member.Property.(*IntegerNode); ok && prop.Value == 0 {
+ if filter, ok := member.Node.(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "find",
+ Arguments: filter.Arguments,
+ Throws: true, // to match the behavior of filter()[0]
+ Map: filter.Map,
+ })
+ }
+ }
+ }
+ if first, ok := (*node).(*BuiltinNode); ok &&
+ first.Name == "first" &&
+ len(first.Arguments) == 1 {
+ if filter, ok := first.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "find",
+ Arguments: filter.Arguments,
+ Throws: false, // as first() will return nil if not found
+ Map: filter.Map,
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/filter_last.go b/vendor/github.com/expr-lang/expr/optimizer/filter_last.go
new file mode 100644
index 00000000..9a1cc5e2
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/filter_last.go
@@ -0,0 +1,38 @@
+package optimizer
+
+import (
+ . "github.com/expr-lang/expr/ast"
+)
+
+type filterLast struct{}
+
+func (*filterLast) Visit(node *Node) {
+ if member, ok := (*node).(*MemberNode); ok && member.Property != nil && !member.Optional {
+ if prop, ok := member.Property.(*IntegerNode); ok && prop.Value == -1 {
+ if filter, ok := member.Node.(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "findLast",
+ Arguments: filter.Arguments,
+ Throws: true, // to match the behavior of filter()[-1]
+ Map: filter.Map,
+ })
+ }
+ }
+ }
+ if first, ok := (*node).(*BuiltinNode); ok &&
+ first.Name == "last" &&
+ len(first.Arguments) == 1 {
+ if filter, ok := first.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "findLast",
+ Arguments: filter.Arguments,
+ Throws: false, // as last() will return nil if not found
+ Map: filter.Map,
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/filter_len.go b/vendor/github.com/expr-lang/expr/optimizer/filter_len.go
new file mode 100644
index 00000000..6577163e
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/filter_len.go
@@ -0,0 +1,22 @@
+package optimizer
+
+import (
+ . "github.com/expr-lang/expr/ast"
+)
+
+type filterLen struct{}
+
+func (*filterLen) Visit(node *Node) {
+ if ln, ok := (*node).(*BuiltinNode); ok &&
+ ln.Name == "len" &&
+ len(ln.Arguments) == 1 {
+ if filter, ok := ln.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "count",
+ Arguments: filter.Arguments,
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/filter_map.go b/vendor/github.com/expr-lang/expr/optimizer/filter_map.go
new file mode 100644
index 00000000..d988dc69
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/filter_map.go
@@ -0,0 +1,25 @@
+package optimizer
+
+import (
+ . "github.com/expr-lang/expr/ast"
+)
+
+type filterMap struct{}
+
+func (*filterMap) Visit(node *Node) {
+ if mapBuiltin, ok := (*node).(*BuiltinNode); ok &&
+ mapBuiltin.Name == "map" &&
+ len(mapBuiltin.Arguments) == 2 {
+ if closure, ok := mapBuiltin.Arguments[1].(*ClosureNode); ok {
+ if filter, ok := mapBuiltin.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ filter.Map == nil /* not already optimized */ {
+ Patch(node, &BuiltinNode{
+ Name: "filter",
+ Arguments: filter.Arguments,
+ Map: closure.Node,
+ })
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/fold.go b/vendor/github.com/expr-lang/expr/optimizer/fold.go
new file mode 100644
index 00000000..910c9240
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/fold.go
@@ -0,0 +1,357 @@
+package optimizer
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+
+ . "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/file"
+)
+
+var (
+ integerType = reflect.TypeOf(0)
+ floatType = reflect.TypeOf(float64(0))
+ stringType = reflect.TypeOf("")
+)
+
+type fold struct {
+ applied bool
+ err *file.Error
+}
+
+func (fold *fold) Visit(node *Node) {
+ patch := func(newNode Node) {
+ fold.applied = true
+ Patch(node, newNode)
+ }
+ patchWithType := func(newNode Node) {
+ patch(newNode)
+ switch newNode.(type) {
+ case *IntegerNode:
+ newNode.SetType(integerType)
+ case *FloatNode:
+ newNode.SetType(floatType)
+ case *StringNode:
+ newNode.SetType(stringType)
+ default:
+ panic(fmt.Sprintf("unknown type %T", newNode))
+ }
+ }
+
+ switch n := (*node).(type) {
+ case *UnaryNode:
+ switch n.Operator {
+ case "-":
+ if i, ok := n.Node.(*IntegerNode); ok {
+ patchWithType(&IntegerNode{Value: -i.Value})
+ }
+ if i, ok := n.Node.(*FloatNode); ok {
+ patchWithType(&FloatNode{Value: -i.Value})
+ }
+ case "+":
+ if i, ok := n.Node.(*IntegerNode); ok {
+ patchWithType(&IntegerNode{Value: i.Value})
+ }
+ if i, ok := n.Node.(*FloatNode); ok {
+ patchWithType(&FloatNode{Value: i.Value})
+ }
+ case "!", "not":
+ if a := toBool(n.Node); a != nil {
+ patch(&BoolNode{Value: !a.Value})
+ }
+ }
+
+ case *BinaryNode:
+ switch n.Operator {
+ case "+":
+ {
+ a := toInteger(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&IntegerNode{Value: a.Value + b.Value})
+ }
+ }
+ {
+ a := toInteger(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: float64(a.Value) + b.Value})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value + float64(b.Value)})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value + b.Value})
+ }
+ }
+ {
+ a := toString(n.Left)
+ b := toString(n.Right)
+ if a != nil && b != nil {
+ patch(&StringNode{Value: a.Value + b.Value})
+ }
+ }
+ case "-":
+ {
+ a := toInteger(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&IntegerNode{Value: a.Value - b.Value})
+ }
+ }
+ {
+ a := toInteger(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: float64(a.Value) - b.Value})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value - float64(b.Value)})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value - b.Value})
+ }
+ }
+ case "*":
+ {
+ a := toInteger(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&IntegerNode{Value: a.Value * b.Value})
+ }
+ }
+ {
+ a := toInteger(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: float64(a.Value) * b.Value})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value * float64(b.Value)})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value * b.Value})
+ }
+ }
+ case "/":
+ {
+ a := toInteger(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: float64(a.Value) / float64(b.Value)})
+ }
+ }
+ {
+ a := toInteger(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: float64(a.Value) / b.Value})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value / float64(b.Value)})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: a.Value / b.Value})
+ }
+ }
+ case "%":
+ if a, ok := n.Left.(*IntegerNode); ok {
+ if b, ok := n.Right.(*IntegerNode); ok {
+ if b.Value == 0 {
+ fold.err = &file.Error{
+ Location: (*node).Location(),
+ Message: "integer divide by zero",
+ }
+ return
+ }
+ patch(&IntegerNode{Value: a.Value % b.Value})
+ }
+ }
+ case "**", "^":
+ {
+ a := toInteger(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: math.Pow(float64(a.Value), float64(b.Value))})
+ }
+ }
+ {
+ a := toInteger(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: math.Pow(float64(a.Value), b.Value)})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: math.Pow(a.Value, float64(b.Value))})
+ }
+ }
+ {
+ a := toFloat(n.Left)
+ b := toFloat(n.Right)
+ if a != nil && b != nil {
+ patchWithType(&FloatNode{Value: math.Pow(a.Value, b.Value)})
+ }
+ }
+ case "and", "&&":
+ a := toBool(n.Left)
+ b := toBool(n.Right)
+
+ if a != nil && a.Value { // true and x
+ patch(n.Right)
+ } else if b != nil && b.Value { // x and true
+ patch(n.Left)
+ } else if (a != nil && !a.Value) || (b != nil && !b.Value) { // "x and false" or "false and x"
+ patch(&BoolNode{Value: false})
+ }
+ case "or", "||":
+ a := toBool(n.Left)
+ b := toBool(n.Right)
+
+ if a != nil && !a.Value { // false or x
+ patch(n.Right)
+ } else if b != nil && !b.Value { // x or false
+ patch(n.Left)
+ } else if (a != nil && a.Value) || (b != nil && b.Value) { // "x or true" or "true or x"
+ patch(&BoolNode{Value: true})
+ }
+ case "==":
+ {
+ a := toInteger(n.Left)
+ b := toInteger(n.Right)
+ if a != nil && b != nil {
+ patch(&BoolNode{Value: a.Value == b.Value})
+ }
+ }
+ {
+ a := toString(n.Left)
+ b := toString(n.Right)
+ if a != nil && b != nil {
+ patch(&BoolNode{Value: a.Value == b.Value})
+ }
+ }
+ {
+ a := toBool(n.Left)
+ b := toBool(n.Right)
+ if a != nil && b != nil {
+ patch(&BoolNode{Value: a.Value == b.Value})
+ }
+ }
+ }
+
+ case *ArrayNode:
+ if len(n.Nodes) > 0 {
+ for _, a := range n.Nodes {
+ switch a.(type) {
+ case *IntegerNode, *FloatNode, *StringNode, *BoolNode:
+ continue
+ default:
+ return
+ }
+ }
+ value := make([]any, len(n.Nodes))
+ for i, a := range n.Nodes {
+ switch b := a.(type) {
+ case *IntegerNode:
+ value[i] = b.Value
+ case *FloatNode:
+ value[i] = b.Value
+ case *StringNode:
+ value[i] = b.Value
+ case *BoolNode:
+ value[i] = b.Value
+ }
+ }
+ patch(&ConstantNode{Value: value})
+ }
+
+ case *BuiltinNode:
+ switch n.Name {
+ case "filter":
+ if len(n.Arguments) != 2 {
+ return
+ }
+ if base, ok := n.Arguments[0].(*BuiltinNode); ok && base.Name == "filter" {
+ patch(&BuiltinNode{
+ Name: "filter",
+ Arguments: []Node{
+ base.Arguments[0],
+ &BinaryNode{
+ Operator: "&&",
+ Left: base.Arguments[1],
+ Right: n.Arguments[1],
+ },
+ },
+ })
+ }
+ }
+ }
+}
+
+func toString(n Node) *StringNode {
+ switch a := n.(type) {
+ case *StringNode:
+ return a
+ }
+ return nil
+}
+
+func toInteger(n Node) *IntegerNode {
+ switch a := n.(type) {
+ case *IntegerNode:
+ return a
+ }
+ return nil
+}
+
+func toFloat(n Node) *FloatNode {
+ switch a := n.(type) {
+ case *FloatNode:
+ return a
+ }
+ return nil
+}
+
+func toBool(n Node) *BoolNode {
+ switch a := n.(type) {
+ case *BoolNode:
+ return a
+ }
+ return nil
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/in_array.go b/vendor/github.com/expr-lang/expr/optimizer/in_array.go
new file mode 100644
index 00000000..8933d9b9
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/in_array.go
@@ -0,0 +1,64 @@
+package optimizer
+
+import (
+ "reflect"
+
+ . "github.com/expr-lang/expr/ast"
+)
+
+type inArray struct{}
+
+func (*inArray) Visit(node *Node) {
+ switch n := (*node).(type) {
+ case *BinaryNode:
+ if n.Operator == "in" {
+ if array, ok := n.Right.(*ArrayNode); ok {
+ if len(array.Nodes) > 0 {
+ t := n.Left.Type()
+ if t == nil || t.Kind() != reflect.Int {
+ // This optimization can be only performed if left side is int type,
+ // as runtime.in func uses reflect.Map.MapIndex and keys of map must,
+ // be same as checked value type.
+ goto string
+ }
+
+ for _, a := range array.Nodes {
+ if _, ok := a.(*IntegerNode); !ok {
+ goto string
+ }
+ }
+ {
+ value := make(map[int]struct{})
+ for _, a := range array.Nodes {
+ value[a.(*IntegerNode).Value] = struct{}{}
+ }
+ Patch(node, &BinaryNode{
+ Operator: n.Operator,
+ Left: n.Left,
+ Right: &ConstantNode{Value: value},
+ })
+ }
+
+ string:
+ for _, a := range array.Nodes {
+ if _, ok := a.(*StringNode); !ok {
+ return
+ }
+ }
+ {
+ value := make(map[string]struct{})
+ for _, a := range array.Nodes {
+ value[a.(*StringNode).Value] = struct{}{}
+ }
+ Patch(node, &BinaryNode{
+ Operator: n.Operator,
+ Left: n.Left,
+ Right: &ConstantNode{Value: value},
+ })
+ }
+
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/in_range.go b/vendor/github.com/expr-lang/expr/optimizer/in_range.go
new file mode 100644
index 00000000..01faabbd
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/in_range.go
@@ -0,0 +1,43 @@
+package optimizer
+
+import (
+ "reflect"
+
+ . "github.com/expr-lang/expr/ast"
+)
+
+type inRange struct{}
+
+func (*inRange) Visit(node *Node) {
+ switch n := (*node).(type) {
+ case *BinaryNode:
+ if n.Operator == "in" {
+ t := n.Left.Type()
+ if t == nil {
+ return
+ }
+ if t.Kind() != reflect.Int {
+ return
+ }
+ if rangeOp, ok := n.Right.(*BinaryNode); ok && rangeOp.Operator == ".." {
+ if from, ok := rangeOp.Left.(*IntegerNode); ok {
+ if to, ok := rangeOp.Right.(*IntegerNode); ok {
+ Patch(node, &BinaryNode{
+ Operator: "and",
+ Left: &BinaryNode{
+ Operator: ">=",
+ Left: n.Left,
+ Right: from,
+ },
+ Right: &BinaryNode{
+ Operator: "<=",
+ Left: n.Left,
+ Right: to,
+ },
+ })
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/optimizer.go b/vendor/github.com/expr-lang/expr/optimizer/optimizer.go
new file mode 100644
index 00000000..4ceb3fa4
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/optimizer.go
@@ -0,0 +1,43 @@
+package optimizer
+
+import (
+ . "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/conf"
+)
+
+func Optimize(node *Node, config *conf.Config) error {
+ Walk(node, &inArray{})
+ for limit := 1000; limit >= 0; limit-- {
+ fold := &fold{}
+ Walk(node, fold)
+ if fold.err != nil {
+ return fold.err
+ }
+ if !fold.applied {
+ break
+ }
+ }
+ if config != nil && len(config.ConstFns) > 0 {
+ for limit := 100; limit >= 0; limit-- {
+ constExpr := &constExpr{
+ fns: config.ConstFns,
+ }
+ Walk(node, constExpr)
+ if constExpr.err != nil {
+ return constExpr.err
+ }
+ if !constExpr.applied {
+ break
+ }
+ }
+ }
+ Walk(node, &inRange{})
+ Walk(node, &filterMap{})
+ Walk(node, &filterLen{})
+ Walk(node, &filterLast{})
+ Walk(node, &filterFirst{})
+ Walk(node, &predicateCombination{})
+ Walk(node, &sumArray{})
+ Walk(node, &sumMap{})
+ return nil
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/predicate_combination.go b/vendor/github.com/expr-lang/expr/optimizer/predicate_combination.go
new file mode 100644
index 00000000..6e8a7f7c
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/predicate_combination.go
@@ -0,0 +1,61 @@
+package optimizer
+
+import (
+ . "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/parser/operator"
+)
+
+/*
+predicateCombination is a visitor that combines multiple predicate calls into a single call.
+For example, the following expression:
+
+ all(x, x > 1) && all(x, x < 10) -> all(x, x > 1 && x < 10)
+ any(x, x > 1) || any(x, x < 10) -> any(x, x > 1 || x < 10)
+ none(x, x > 1) && none(x, x < 10) -> none(x, x > 1 || x < 10)
+*/
+type predicateCombination struct{}
+
+func (v *predicateCombination) Visit(node *Node) {
+ if op, ok := (*node).(*BinaryNode); ok && operator.IsBoolean(op.Operator) {
+ if left, ok := op.Left.(*BuiltinNode); ok {
+ if combinedOp, ok := combinedOperator(left.Name, op.Operator); ok {
+ if right, ok := op.Right.(*BuiltinNode); ok && right.Name == left.Name {
+ if left.Arguments[0].Type() == right.Arguments[0].Type() && left.Arguments[0].String() == right.Arguments[0].String() {
+ closure := &ClosureNode{
+ Node: &BinaryNode{
+ Operator: combinedOp,
+ Left: left.Arguments[1].(*ClosureNode).Node,
+ Right: right.Arguments[1].(*ClosureNode).Node,
+ },
+ }
+ v.Visit(&closure.Node)
+ Patch(node, &BuiltinNode{
+ Name: left.Name,
+ Arguments: []Node{
+ left.Arguments[0],
+ closure,
+ },
+ })
+ }
+ }
+ }
+ }
+ }
+}
+
+func combinedOperator(fn, op string) (string, bool) {
+ switch {
+ case fn == "all" && (op == "and" || op == "&&"):
+ return op, true
+ case fn == "any" && (op == "or" || op == "||"):
+ return op, true
+ case fn == "none" && (op == "and" || op == "&&"):
+ switch op {
+ case "and":
+ return "or", true
+ case "&&":
+ return "||", true
+ }
+ }
+ return "", false
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/sum_array.go b/vendor/github.com/expr-lang/expr/optimizer/sum_array.go
new file mode 100644
index 00000000..0a05d1f2
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/sum_array.go
@@ -0,0 +1,37 @@
+package optimizer
+
+import (
+ "fmt"
+
+ . "github.com/expr-lang/expr/ast"
+)
+
+type sumArray struct{}
+
+func (*sumArray) Visit(node *Node) {
+ if sumBuiltin, ok := (*node).(*BuiltinNode); ok &&
+ sumBuiltin.Name == "sum" &&
+ len(sumBuiltin.Arguments) == 1 {
+ if array, ok := sumBuiltin.Arguments[0].(*ArrayNode); ok &&
+ len(array.Nodes) >= 2 {
+ Patch(node, sumArrayFold(array))
+ }
+ }
+}
+
+func sumArrayFold(array *ArrayNode) *BinaryNode {
+ if len(array.Nodes) > 2 {
+ return &BinaryNode{
+ Operator: "+",
+ Left: array.Nodes[0],
+ Right: sumArrayFold(&ArrayNode{Nodes: array.Nodes[1:]}),
+ }
+ } else if len(array.Nodes) == 2 {
+ return &BinaryNode{
+ Operator: "+",
+ Left: array.Nodes[0],
+ Right: array.Nodes[1],
+ }
+ }
+ panic(fmt.Errorf("sumArrayFold: invalid array length %d", len(array.Nodes)))
+}
diff --git a/vendor/github.com/expr-lang/expr/optimizer/sum_map.go b/vendor/github.com/expr-lang/expr/optimizer/sum_map.go
new file mode 100644
index 00000000..a41a5373
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/optimizer/sum_map.go
@@ -0,0 +1,25 @@
+package optimizer
+
+import (
+ . "github.com/expr-lang/expr/ast"
+)
+
+type sumMap struct{}
+
+func (*sumMap) Visit(node *Node) {
+ if sumBuiltin, ok := (*node).(*BuiltinNode); ok &&
+ sumBuiltin.Name == "sum" &&
+ len(sumBuiltin.Arguments) == 1 {
+ if mapBuiltin, ok := sumBuiltin.Arguments[0].(*BuiltinNode); ok &&
+ mapBuiltin.Name == "map" &&
+ len(mapBuiltin.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "sum",
+ Arguments: []Node{
+ mapBuiltin.Arguments[0],
+ mapBuiltin.Arguments[1],
+ },
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/parser/lexer/lexer.go b/vendor/github.com/expr-lang/expr/parser/lexer/lexer.go
new file mode 100644
index 00000000..e6b06c09
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/parser/lexer/lexer.go
@@ -0,0 +1,230 @@
+package lexer
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/expr-lang/expr/file"
+)
+
+func Lex(source file.Source) ([]Token, error) {
+ l := &lexer{
+ source: source,
+ tokens: make([]Token, 0),
+ start: 0,
+ end: 0,
+ }
+ l.commit()
+
+ for state := root; state != nil; {
+ state = state(l)
+ }
+
+ if l.err != nil {
+ return nil, l.err.Bind(source)
+ }
+
+ return l.tokens, nil
+}
+
+type lexer struct {
+ source file.Source
+ tokens []Token
+ start, end int
+ err *file.Error
+}
+
+const eof rune = -1
+
+func (l *lexer) commit() {
+ l.start = l.end
+}
+
+func (l *lexer) next() rune {
+ if l.end >= len(l.source) {
+ l.end++
+ return eof
+ }
+ r := l.source[l.end]
+ l.end++
+ return r
+}
+
+func (l *lexer) peek() rune {
+ r := l.next()
+ l.backup()
+ return r
+}
+
+func (l *lexer) backup() {
+ l.end--
+}
+
+func (l *lexer) emit(t Kind) {
+ l.emitValue(t, l.word())
+}
+
+func (l *lexer) emitValue(t Kind, value string) {
+ l.tokens = append(l.tokens, Token{
+ Location: file.Location{From: l.start, To: l.end},
+ Kind: t,
+ Value: value,
+ })
+ l.commit()
+}
+
+func (l *lexer) emitEOF() {
+ from := l.end - 2
+ if from < 0 {
+ from = 0
+ }
+ to := l.end - 1
+ if to < 0 {
+ to = 0
+ }
+ l.tokens = append(l.tokens, Token{
+ Location: file.Location{From: from, To: to},
+ Kind: EOF,
+ })
+ l.commit()
+}
+
+func (l *lexer) skip() {
+ l.commit()
+}
+
+func (l *lexer) word() string {
+ // TODO: boundary check is NOT needed here, but for some reason CI fuzz tests are failing.
+ if l.start > len(l.source) || l.end > len(l.source) {
+ return "__invalid__"
+ }
+ return string(l.source[l.start:l.end])
+}
+
+func (l *lexer) accept(valid string) bool {
+ if strings.ContainsRune(valid, l.next()) {
+ return true
+ }
+ l.backup()
+ return false
+}
+
+func (l *lexer) acceptRun(valid string) {
+ for strings.ContainsRune(valid, l.next()) {
+ }
+ l.backup()
+}
+
+func (l *lexer) skipSpaces() {
+ r := l.peek()
+ for ; r == ' '; r = l.peek() {
+ l.next()
+ }
+ l.skip()
+}
+
+func (l *lexer) acceptWord(word string) bool {
+ pos := l.end
+
+ l.skipSpaces()
+
+ for _, ch := range word {
+ if l.next() != ch {
+ l.end = pos
+ return false
+ }
+ }
+ if r := l.peek(); r != ' ' && r != eof {
+ l.end = pos
+ return false
+ }
+
+ return true
+}
+
+func (l *lexer) error(format string, args ...any) stateFn {
+ if l.err == nil { // show first error
+ l.err = &file.Error{
+ Location: file.Location{
+ From: l.end - 1,
+ To: l.end,
+ },
+ Message: fmt.Sprintf(format, args...),
+ }
+ }
+ return nil
+}
+
+func digitVal(ch rune) int {
+ switch {
+ case '0' <= ch && ch <= '9':
+ return int(ch - '0')
+ case 'a' <= lower(ch) && lower(ch) <= 'f':
+ return int(lower(ch) - 'a' + 10)
+ }
+ return 16 // larger than any legal digit val
+}
+
+func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter
+
+func (l *lexer) scanDigits(ch rune, base, n int) rune {
+ for n > 0 && digitVal(ch) < base {
+ ch = l.next()
+ n--
+ }
+ if n > 0 {
+ l.error("invalid char escape")
+ }
+ return ch
+}
+
+func (l *lexer) scanEscape(quote rune) rune {
+ ch := l.next() // read character after '/'
+ switch ch {
+ case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
+ // nothing to do
+ ch = l.next()
+ case '0', '1', '2', '3', '4', '5', '6', '7':
+ ch = l.scanDigits(ch, 8, 3)
+ case 'x':
+ ch = l.scanDigits(l.next(), 16, 2)
+ case 'u':
+ ch = l.scanDigits(l.next(), 16, 4)
+ case 'U':
+ ch = l.scanDigits(l.next(), 16, 8)
+ default:
+ l.error("invalid char escape")
+ }
+ return ch
+}
+
+func (l *lexer) scanString(quote rune) (n int) {
+ ch := l.next() // read character after quote
+ for ch != quote {
+ if ch == '\n' || ch == eof {
+ l.error("literal not terminated")
+ return
+ }
+ if ch == '\\' {
+ ch = l.scanEscape(quote)
+ } else {
+ ch = l.next()
+ }
+ n++
+ }
+ return
+}
+
+func (l *lexer) scanRawString(quote rune) (n int) {
+ ch := l.next() // read character after back tick
+ for ch != quote {
+ if ch == eof {
+ l.error("literal not terminated")
+ return
+ }
+ ch = l.next()
+ n++
+ }
+ l.emitValue(String, string(l.source[l.start+1:l.end-1]))
+ return
+}
diff --git a/vendor/github.com/expr-lang/expr/parser/lexer/state.go b/vendor/github.com/expr-lang/expr/parser/lexer/state.go
new file mode 100644
index 00000000..d351e2f5
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/parser/lexer/state.go
@@ -0,0 +1,228 @@
+package lexer
+
+import (
+ "strings"
+
+ "github.com/expr-lang/expr/parser/utils"
+)
+
+type stateFn func(*lexer) stateFn
+
+func root(l *lexer) stateFn {
+ switch r := l.next(); {
+ case r == eof:
+ l.emitEOF()
+ return nil
+ case utils.IsSpace(r):
+ l.skip()
+ return root
+ case r == '\'' || r == '"':
+ l.scanString(r)
+ str, err := unescape(l.word())
+ if err != nil {
+ l.error("%v", err)
+ }
+ l.emitValue(String, str)
+ case r == '`':
+ l.scanRawString(r)
+ case '0' <= r && r <= '9':
+ l.backup()
+ return number
+ case r == '?':
+ return questionMark
+ case r == '/':
+ return slash
+ case r == '#':
+ return pointer
+ case r == '|':
+ l.accept("|")
+ l.emit(Operator)
+ case r == ':':
+ l.accept(":")
+ l.emit(Operator)
+ case strings.ContainsRune("([{", r):
+ l.emit(Bracket)
+ case strings.ContainsRune(")]}", r):
+ l.emit(Bracket)
+ case strings.ContainsRune(",;%+-^", r): // single rune operator
+ l.emit(Operator)
+ case strings.ContainsRune("&!=*<>", r): // possible double rune operator
+ l.accept("&=*")
+ l.emit(Operator)
+ case r == '.':
+ l.backup()
+ return dot
+ case utils.IsAlphaNumeric(r):
+ l.backup()
+ return identifier
+ default:
+ return l.error("unrecognized character: %#U", r)
+ }
+ return root
+}
+
+func number(l *lexer) stateFn {
+ if !l.scanNumber() {
+ return l.error("bad number syntax: %q", l.word())
+ }
+ l.emit(Number)
+ return root
+}
+
+func (l *lexer) scanNumber() bool {
+ digits := "0123456789_"
+ // Is it hex?
+ if l.accept("0") {
+ // Note: Leading 0 does not mean octal in floats.
+ if l.accept("xX") {
+ digits = "0123456789abcdefABCDEF_"
+ } else if l.accept("oO") {
+ digits = "01234567_"
+ } else if l.accept("bB") {
+ digits = "01_"
+ }
+ }
+ l.acceptRun(digits)
+ end := l.end
+ if l.accept(".") {
+ // Lookup for .. operator: if after dot there is another dot (1..2), it maybe a range operator.
+ if l.peek() == '.' {
+ // We can't backup() here, as it would require two backups,
+ // and backup() func supports only one for now. So, save and
+ // restore it here.
+ l.end = end
+ return true
+ }
+ l.acceptRun(digits)
+ }
+ if l.accept("eE") {
+ l.accept("+-")
+ l.acceptRun(digits)
+ }
+ // Next thing mustn't be alphanumeric.
+ if utils.IsAlphaNumeric(l.peek()) {
+ l.next()
+ return false
+ }
+ return true
+}
+
+func dot(l *lexer) stateFn {
+ l.next()
+ if l.accept("0123456789") {
+ l.backup()
+ return number
+ }
+ l.accept(".")
+ l.emit(Operator)
+ return root
+}
+
+func identifier(l *lexer) stateFn {
+loop:
+ for {
+ switch r := l.next(); {
+ case utils.IsAlphaNumeric(r):
+ // absorb
+ default:
+ l.backup()
+ switch l.word() {
+ case "not":
+ return not
+ case "in", "or", "and", "matches", "contains", "startsWith", "endsWith":
+ l.emit(Operator)
+ case "let":
+ l.emit(Operator)
+ default:
+ l.emit(Identifier)
+ }
+ break loop
+ }
+ }
+ return root
+}
+
+func not(l *lexer) stateFn {
+ l.emit(Operator)
+
+ l.skipSpaces()
+
+ end := l.end
+
+ // Get the next word.
+ for {
+ r := l.next()
+ if utils.IsAlphaNumeric(r) {
+ // absorb
+ } else {
+ l.backup()
+ break
+ }
+ }
+
+ switch l.word() {
+ case "in", "matches", "contains", "startsWith", "endsWith":
+ l.emit(Operator)
+ default:
+ l.end = end
+ }
+ return root
+}
+
+func questionMark(l *lexer) stateFn {
+ l.accept(".?")
+ l.emit(Operator)
+ return root
+}
+
+func slash(l *lexer) stateFn {
+ if l.accept("/") {
+ return singleLineComment
+ }
+ if l.accept("*") {
+ return multiLineComment
+ }
+ l.emit(Operator)
+ return root
+}
+
+func singleLineComment(l *lexer) stateFn {
+ for {
+ r := l.next()
+ if r == eof || r == '\n' {
+ break
+ }
+ }
+ l.skip()
+ return root
+}
+
+func multiLineComment(l *lexer) stateFn {
+ for {
+ r := l.next()
+ if r == eof {
+ return l.error("unclosed comment")
+ }
+ if r == '*' && l.accept("/") {
+ break
+ }
+ }
+ l.skip()
+ return root
+}
+
+func pointer(l *lexer) stateFn {
+ l.accept("#")
+ l.emit(Operator)
+ for {
+ switch r := l.next(); {
+ case utils.IsAlphaNumeric(r): // absorb
+ default:
+ l.backup()
+ if l.word() != "" {
+ l.emit(Identifier)
+ }
+ return root
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/parser/lexer/token.go b/vendor/github.com/expr-lang/expr/parser/lexer/token.go
new file mode 100644
index 00000000..459fa690
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/parser/lexer/token.go
@@ -0,0 +1,47 @@
+package lexer
+
+import (
+ "fmt"
+
+ "github.com/expr-lang/expr/file"
+)
+
+type Kind string
+
+const (
+ Identifier Kind = "Identifier"
+ Number Kind = "Number"
+ String Kind = "String"
+ Operator Kind = "Operator"
+ Bracket Kind = "Bracket"
+ EOF Kind = "EOF"
+)
+
+type Token struct {
+ file.Location
+ Kind Kind
+ Value string
+}
+
+func (t Token) String() string {
+ if t.Value == "" {
+ return string(t.Kind)
+ }
+ return fmt.Sprintf("%s(%#v)", t.Kind, t.Value)
+}
+
+func (t Token) Is(kind Kind, values ...string) bool {
+ if len(values) == 0 {
+ return kind == t.Kind
+ }
+
+ for _, v := range values {
+ if v == t.Value {
+ goto found
+ }
+ }
+ return false
+
+found:
+ return kind == t.Kind
+}
diff --git a/vendor/github.com/expr-lang/expr/parser/lexer/utils.go b/vendor/github.com/expr-lang/expr/parser/lexer/utils.go
new file mode 100644
index 00000000..5c9e6b59
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/parser/lexer/utils.go
@@ -0,0 +1,186 @@
+package lexer
+
+import (
+ "fmt"
+ "math"
+ "strings"
+ "unicode/utf8"
+)
+
+var (
+ newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n")
+)
+
+// Unescape takes a quoted string, unquotes, and unescapes it.
+func unescape(value string) (string, error) {
+ // All strings normalize newlines to the \n representation.
+ value = newlineNormalizer.Replace(value)
+ n := len(value)
+
+ // Nothing to unescape / decode.
+ if n < 2 {
+ return value, fmt.Errorf("unable to unescape string")
+ }
+
+ // Quoted string of some form, must have same first and last char.
+ if value[0] != value[n-1] || (value[0] != '"' && value[0] != '\'') {
+ return value, fmt.Errorf("unable to unescape string")
+ }
+
+ value = value[1 : n-1]
+
+ // The string contains escape characters.
+ // The following logic is adapted from `strconv/quote.go`
+ var runeTmp [utf8.UTFMax]byte
+ size := 3 * uint64(n) / 2
+ if size >= math.MaxInt {
+ return "", fmt.Errorf("too large string")
+ }
+ buf := make([]byte, 0, size)
+ for len(value) > 0 {
+ c, multibyte, rest, err := unescapeChar(value)
+ if err != nil {
+ return "", err
+ }
+ value = rest
+ if c < utf8.RuneSelf || !multibyte {
+ buf = append(buf, byte(c))
+ } else {
+ n := utf8.EncodeRune(runeTmp[:], c)
+ buf = append(buf, runeTmp[:n]...)
+ }
+ }
+ return string(buf), nil
+}
+
+// unescapeChar takes a string input and returns the following info:
+//
+// value - the escaped unicode rune at the front of the string.
+// multibyte - whether the rune value might require multiple bytes to represent.
+// tail - the remainder of the input string.
+// err - error value, if the character could not be unescaped.
+//
+// When multibyte is true the return value may still fit within a single byte,
+// but a multibyte conversion is attempted which is more expensive than when the
+// value is known to fit within one byte.
+func unescapeChar(s string) (value rune, multibyte bool, tail string, err error) {
+ // 1. Character is not an escape sequence.
+ switch c := s[0]; {
+ case c >= utf8.RuneSelf:
+ r, size := utf8.DecodeRuneInString(s)
+ return r, true, s[size:], nil
+ case c != '\\':
+ return rune(s[0]), false, s[1:], nil
+ }
+
+ // 2. Last character is the start of an escape sequence.
+ if len(s) <= 1 {
+ err = fmt.Errorf("unable to unescape string, found '\\' as last character")
+ return
+ }
+
+ c := s[1]
+ s = s[2:]
+ // 3. Common escape sequences shared with Google SQL
+ switch c {
+ case 'a':
+ value = '\a'
+ case 'b':
+ value = '\b'
+ case 'f':
+ value = '\f'
+ case 'n':
+ value = '\n'
+ case 'r':
+ value = '\r'
+ case 't':
+ value = '\t'
+ case 'v':
+ value = '\v'
+ case '\\':
+ value = '\\'
+ case '\'':
+ value = '\''
+ case '"':
+ value = '"'
+ case '`':
+ value = '`'
+ case '?':
+ value = '?'
+
+ // 4. Unicode escape sequences, reproduced from `strconv/quote.go`
+ case 'x', 'X', 'u', 'U':
+ n := 0
+ switch c {
+ case 'x', 'X':
+ n = 2
+ case 'u':
+ n = 4
+ case 'U':
+ n = 8
+ }
+ var v rune
+ if len(s) < n {
+ err = fmt.Errorf("unable to unescape string")
+ return
+ }
+ for j := 0; j < n; j++ {
+ x, ok := unhex(s[j])
+ if !ok {
+ err = fmt.Errorf("unable to unescape string")
+ return
+ }
+ v = v<<4 | x
+ }
+ s = s[n:]
+ if v > utf8.MaxRune {
+ err = fmt.Errorf("unable to unescape string")
+ return
+ }
+ value = v
+ multibyte = true
+
+ // 5. Octal escape sequences, must be three digits \[0-3][0-7][0-7]
+ case '0', '1', '2', '3':
+ if len(s) < 2 {
+ err = fmt.Errorf("unable to unescape octal sequence in string")
+ return
+ }
+ v := rune(c - '0')
+ for j := 0; j < 2; j++ {
+ x := s[j]
+ if x < '0' || x > '7' {
+ err = fmt.Errorf("unable to unescape octal sequence in string")
+ return
+ }
+ v = v*8 + rune(x-'0')
+ }
+ if v > utf8.MaxRune {
+ err = fmt.Errorf("unable to unescape string")
+ return
+ }
+ value = v
+ s = s[2:]
+ multibyte = true
+
+ // Unknown escape sequence.
+ default:
+ err = fmt.Errorf("unable to unescape string")
+ }
+
+ tail = s
+ return
+}
+
+func unhex(b byte) (rune, bool) {
+ c := rune(b)
+ switch {
+ case '0' <= c && c <= '9':
+ return c - '0', true
+ case 'a' <= c && c <= 'f':
+ return c - 'a' + 10, true
+ case 'A' <= c && c <= 'F':
+ return c - 'A' + 10, true
+ }
+ return 0, false
+}
diff --git a/vendor/github.com/expr-lang/expr/parser/operator/operator.go b/vendor/github.com/expr-lang/expr/parser/operator/operator.go
new file mode 100644
index 00000000..4eeaf80e
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/parser/operator/operator.go
@@ -0,0 +1,69 @@
+package operator
+
+type Associativity int
+
+const (
+ Left Associativity = iota + 1
+ Right
+)
+
+type Operator struct {
+ Precedence int
+ Associativity Associativity
+}
+
+func Less(a, b string) bool {
+ return Binary[a].Precedence < Binary[b].Precedence
+}
+
+func IsBoolean(op string) bool {
+ return op == "and" || op == "or" || op == "&&" || op == "||"
+}
+
+func AllowedNegateSuffix(op string) bool {
+ switch op {
+ case "contains", "matches", "startsWith", "endsWith", "in":
+ return true
+ default:
+ return false
+ }
+}
+
+var Unary = map[string]Operator{
+ "not": {50, Left},
+ "!": {50, Left},
+ "-": {90, Left},
+ "+": {90, Left},
+}
+
+var Binary = map[string]Operator{
+ "|": {0, Left},
+ "or": {10, Left},
+ "||": {10, Left},
+ "and": {15, Left},
+ "&&": {15, Left},
+ "==": {20, Left},
+ "!=": {20, Left},
+ "<": {20, Left},
+ ">": {20, Left},
+ ">=": {20, Left},
+ "<=": {20, Left},
+ "in": {20, Left},
+ "matches": {20, Left},
+ "contains": {20, Left},
+ "startsWith": {20, Left},
+ "endsWith": {20, Left},
+ "..": {25, Left},
+ "+": {30, Left},
+ "-": {30, Left},
+ "*": {60, Left},
+ "/": {60, Left},
+ "%": {60, Left},
+ "**": {100, Right},
+ "^": {100, Right},
+ "??": {500, Left},
+}
+
+func IsComparison(op string) bool {
+ return op == "<" || op == ">" || op == ">=" || op == "<="
+}
diff --git a/vendor/github.com/expr-lang/expr/parser/parser.go b/vendor/github.com/expr-lang/expr/parser/parser.go
new file mode 100644
index 00000000..77b2a700
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/parser/parser.go
@@ -0,0 +1,733 @@
+package parser
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+
+ . "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/conf"
+ "github.com/expr-lang/expr/file"
+ . "github.com/expr-lang/expr/parser/lexer"
+ "github.com/expr-lang/expr/parser/operator"
+ "github.com/expr-lang/expr/parser/utils"
+)
+
+type arg byte
+
+const (
+ expr arg = 1 << iota
+ closure
+)
+
+const optional arg = 1 << 7
+
+var predicates = map[string]struct {
+ args []arg
+}{
+ "all": {[]arg{expr, closure}},
+ "none": {[]arg{expr, closure}},
+ "any": {[]arg{expr, closure}},
+ "one": {[]arg{expr, closure}},
+ "filter": {[]arg{expr, closure}},
+ "map": {[]arg{expr, closure}},
+ "count": {[]arg{expr, closure | optional}},
+ "sum": {[]arg{expr, closure | optional}},
+ "find": {[]arg{expr, closure}},
+ "findIndex": {[]arg{expr, closure}},
+ "findLast": {[]arg{expr, closure}},
+ "findLastIndex": {[]arg{expr, closure}},
+ "groupBy": {[]arg{expr, closure}},
+ "sortBy": {[]arg{expr, closure, expr | optional}},
+ "reduce": {[]arg{expr, closure, expr | optional}},
+}
+
+type parser struct {
+ tokens []Token
+ current Token
+ pos int
+ err *file.Error
+ depth int // closure call depth
+ config *conf.Config
+}
+
+type Tree struct {
+ Node Node
+ Source file.Source
+}
+
+func Parse(input string) (*Tree, error) {
+ return ParseWithConfig(input, &conf.Config{
+ Disabled: map[string]bool{},
+ })
+}
+
+func ParseWithConfig(input string, config *conf.Config) (*Tree, error) {
+ source := file.NewSource(input)
+
+ tokens, err := Lex(source)
+ if err != nil {
+ return nil, err
+ }
+
+ p := &parser{
+ tokens: tokens,
+ current: tokens[0],
+ config: config,
+ }
+
+ node := p.parseExpression(0)
+
+ if !p.current.Is(EOF) {
+ p.error("unexpected token %v", p.current)
+ }
+
+ tree := &Tree{
+ Node: node,
+ Source: source,
+ }
+
+ if p.err != nil {
+ return tree, p.err.Bind(source)
+ }
+
+ return tree, nil
+}
+
+func (p *parser) error(format string, args ...any) {
+ p.errorAt(p.current, format, args...)
+}
+
+func (p *parser) errorAt(token Token, format string, args ...any) {
+ if p.err == nil { // show first error
+ p.err = &file.Error{
+ Location: token.Location,
+ Message: fmt.Sprintf(format, args...),
+ }
+ }
+}
+
+func (p *parser) next() {
+ p.pos++
+ if p.pos >= len(p.tokens) {
+ p.error("unexpected end of expression")
+ return
+ }
+ p.current = p.tokens[p.pos]
+}
+
+func (p *parser) expect(kind Kind, values ...string) {
+ if p.current.Is(kind, values...) {
+ p.next()
+ return
+ }
+ p.error("unexpected token %v", p.current)
+}
+
+// parse functions
+
+func (p *parser) parseExpression(precedence int) Node {
+ if precedence == 0 && p.current.Is(Operator, "let") {
+ return p.parseVariableDeclaration()
+ }
+
+ nodeLeft := p.parsePrimary()
+
+ prevOperator := ""
+ opToken := p.current
+ for opToken.Is(Operator) && p.err == nil {
+ negate := opToken.Is(Operator, "not")
+ var notToken Token
+
+ // Handle "not *" operator, like "not in" or "not contains".
+ if negate {
+ currentPos := p.pos
+ p.next()
+ if operator.AllowedNegateSuffix(p.current.Value) {
+ if op, ok := operator.Binary[p.current.Value]; ok && op.Precedence >= precedence {
+ notToken = p.current
+ opToken = p.current
+ } else {
+ p.pos = currentPos
+ p.current = opToken
+ break
+ }
+ } else {
+ p.error("unexpected token %v", p.current)
+ break
+ }
+ }
+
+ if op, ok := operator.Binary[opToken.Value]; ok && op.Precedence >= precedence {
+ p.next()
+
+ if opToken.Value == "|" {
+ identToken := p.current
+ p.expect(Identifier)
+ nodeLeft = p.parseCall(identToken, []Node{nodeLeft}, true)
+ goto next
+ }
+
+ if prevOperator == "??" && opToken.Value != "??" && !opToken.Is(Bracket, "(") {
+ p.errorAt(opToken, "Operator (%v) and coalesce expressions (??) cannot be mixed. Wrap either by parentheses.", opToken.Value)
+ break
+ }
+
+ if operator.IsComparison(opToken.Value) {
+ nodeLeft = p.parseComparison(nodeLeft, opToken, op.Precedence)
+ goto next
+ }
+
+ var nodeRight Node
+ if op.Associativity == operator.Left {
+ nodeRight = p.parseExpression(op.Precedence + 1)
+ } else {
+ nodeRight = p.parseExpression(op.Precedence)
+ }
+
+ nodeLeft = &BinaryNode{
+ Operator: opToken.Value,
+ Left: nodeLeft,
+ Right: nodeRight,
+ }
+ nodeLeft.SetLocation(opToken.Location)
+
+ if negate {
+ nodeLeft = &UnaryNode{
+ Operator: "not",
+ Node: nodeLeft,
+ }
+ nodeLeft.SetLocation(notToken.Location)
+ }
+
+ goto next
+ }
+ break
+
+ next:
+ prevOperator = opToken.Value
+ opToken = p.current
+ }
+
+ if precedence == 0 {
+ nodeLeft = p.parseConditional(nodeLeft)
+ }
+
+ return nodeLeft
+}
+
+func (p *parser) parseVariableDeclaration() Node {
+ p.expect(Operator, "let")
+ variableName := p.current
+ p.expect(Identifier)
+ p.expect(Operator, "=")
+ value := p.parseExpression(0)
+ p.expect(Operator, ";")
+ node := p.parseExpression(0)
+ let := &VariableDeclaratorNode{
+ Name: variableName.Value,
+ Value: value,
+ Expr: node,
+ }
+ let.SetLocation(variableName.Location)
+ return let
+}
+
+func (p *parser) parseConditional(node Node) Node {
+ var expr1, expr2 Node
+ for p.current.Is(Operator, "?") && p.err == nil {
+ p.next()
+
+ if !p.current.Is(Operator, ":") {
+ expr1 = p.parseExpression(0)
+ p.expect(Operator, ":")
+ expr2 = p.parseExpression(0)
+ } else {
+ p.next()
+ expr1 = node
+ expr2 = p.parseExpression(0)
+ }
+
+ node = &ConditionalNode{
+ Cond: node,
+ Exp1: expr1,
+ Exp2: expr2,
+ }
+ }
+ return node
+}
+
+func (p *parser) parsePrimary() Node {
+ token := p.current
+
+ if token.Is(Operator) {
+ if op, ok := operator.Unary[token.Value]; ok {
+ p.next()
+ expr := p.parseExpression(op.Precedence)
+ node := &UnaryNode{
+ Operator: token.Value,
+ Node: expr,
+ }
+ node.SetLocation(token.Location)
+ return p.parsePostfixExpression(node)
+ }
+ }
+
+ if token.Is(Bracket, "(") {
+ p.next()
+ expr := p.parseExpression(0)
+ p.expect(Bracket, ")") // "an opened parenthesis is not properly closed"
+ return p.parsePostfixExpression(expr)
+ }
+
+ if p.depth > 0 {
+ if token.Is(Operator, "#") || token.Is(Operator, ".") {
+ name := ""
+ if token.Is(Operator, "#") {
+ p.next()
+ if p.current.Is(Identifier) {
+ name = p.current.Value
+ p.next()
+ }
+ }
+ node := &PointerNode{Name: name}
+ node.SetLocation(token.Location)
+ return p.parsePostfixExpression(node)
+ }
+ } else {
+ if token.Is(Operator, "#") || token.Is(Operator, ".") {
+ p.error("cannot use pointer accessor outside closure")
+ }
+ }
+
+ if token.Is(Operator, "::") {
+ p.next()
+ token = p.current
+ p.expect(Identifier)
+ return p.parsePostfixExpression(p.parseCall(token, []Node{}, false))
+ }
+
+ return p.parseSecondary()
+}
+
+func (p *parser) parseSecondary() Node {
+ var node Node
+ token := p.current
+
+ switch token.Kind {
+
+ case Identifier:
+ p.next()
+ switch token.Value {
+ case "true":
+ node := &BoolNode{Value: true}
+ node.SetLocation(token.Location)
+ return node
+ case "false":
+ node := &BoolNode{Value: false}
+ node.SetLocation(token.Location)
+ return node
+ case "nil":
+ node := &NilNode{}
+ node.SetLocation(token.Location)
+ return node
+ default:
+ if p.current.Is(Bracket, "(") {
+ node = p.parseCall(token, []Node{}, true)
+ } else {
+ node = &IdentifierNode{Value: token.Value}
+ node.SetLocation(token.Location)
+ }
+ }
+
+ case Number:
+ p.next()
+ value := strings.Replace(token.Value, "_", "", -1)
+ var node Node
+ valueLower := strings.ToLower(value)
+ switch {
+ case strings.HasPrefix(valueLower, "0x"):
+ number, err := strconv.ParseInt(value, 0, 64)
+ if err != nil {
+ p.error("invalid hex literal: %v", err)
+ }
+ node = p.toIntegerNode(number)
+ case strings.ContainsAny(valueLower, ".e"):
+ number, err := strconv.ParseFloat(value, 64)
+ if err != nil {
+ p.error("invalid float literal: %v", err)
+ }
+ node = p.toFloatNode(number)
+ case strings.HasPrefix(valueLower, "0b"):
+ number, err := strconv.ParseInt(value, 0, 64)
+ if err != nil {
+ p.error("invalid binary literal: %v", err)
+ }
+ node = p.toIntegerNode(number)
+ case strings.HasPrefix(valueLower, "0o"):
+ number, err := strconv.ParseInt(value, 0, 64)
+ if err != nil {
+ p.error("invalid octal literal: %v", err)
+ }
+ node = p.toIntegerNode(number)
+ default:
+ number, err := strconv.ParseInt(value, 10, 64)
+ if err != nil {
+ p.error("invalid integer literal: %v", err)
+ }
+ node = p.toIntegerNode(number)
+ }
+ if node != nil {
+ node.SetLocation(token.Location)
+ }
+ return node
+ case String:
+ p.next()
+ node = &StringNode{Value: token.Value}
+ node.SetLocation(token.Location)
+
+ default:
+ if token.Is(Bracket, "[") {
+ node = p.parseArrayExpression(token)
+ } else if token.Is(Bracket, "{") {
+ node = p.parseMapExpression(token)
+ } else {
+ p.error("unexpected token %v", token)
+ }
+ }
+
+ return p.parsePostfixExpression(node)
+}
+
+func (p *parser) toIntegerNode(number int64) Node {
+ if number > math.MaxInt {
+ p.error("integer literal is too large")
+ return nil
+ }
+ return &IntegerNode{Value: int(number)}
+}
+
+func (p *parser) toFloatNode(number float64) Node {
+ if number > math.MaxFloat64 {
+ p.error("float literal is too large")
+ return nil
+ }
+ return &FloatNode{Value: number}
+}
+
+func (p *parser) parseCall(token Token, arguments []Node, checkOverrides bool) Node {
+ var node Node
+
+ isOverridden := p.config.IsOverridden(token.Value)
+ isOverridden = isOverridden && checkOverrides
+
+ if b, ok := predicates[token.Value]; ok && !isOverridden {
+ p.expect(Bracket, "(")
+
+ // In case of the pipe operator, the first argument is the left-hand side
+ // of the operator, so we do not parse it as an argument inside brackets.
+ args := b.args[len(arguments):]
+
+ for i, arg := range args {
+ if arg&optional == optional {
+ if p.current.Is(Bracket, ")") {
+ break
+ }
+ } else {
+ if p.current.Is(Bracket, ")") {
+ p.error("expected at least %d arguments", len(args))
+ }
+ }
+
+ if i > 0 {
+ p.expect(Operator, ",")
+ }
+ var node Node
+ switch {
+ case arg&expr == expr:
+ node = p.parseExpression(0)
+ case arg&closure == closure:
+ node = p.parseClosure()
+ }
+ arguments = append(arguments, node)
+ }
+
+ p.expect(Bracket, ")")
+
+ node = &BuiltinNode{
+ Name: token.Value,
+ Arguments: arguments,
+ }
+ node.SetLocation(token.Location)
+ } else if _, ok := builtin.Index[token.Value]; ok && !p.config.Disabled[token.Value] && !isOverridden {
+ node = &BuiltinNode{
+ Name: token.Value,
+ Arguments: p.parseArguments(arguments),
+ }
+ node.SetLocation(token.Location)
+ } else {
+ callee := &IdentifierNode{Value: token.Value}
+ callee.SetLocation(token.Location)
+ node = &CallNode{
+ Callee: callee,
+ Arguments: p.parseArguments(arguments),
+ }
+ node.SetLocation(token.Location)
+ }
+ return node
+}
+
+func (p *parser) parseArguments(arguments []Node) []Node {
+ // If pipe operator is used, the first argument is the left-hand side
+ // of the operator, so we do not parse it as an argument inside brackets.
+ offset := len(arguments)
+
+ p.expect(Bracket, "(")
+ for !p.current.Is(Bracket, ")") && p.err == nil {
+ if len(arguments) > offset {
+ p.expect(Operator, ",")
+ }
+ node := p.parseExpression(0)
+ arguments = append(arguments, node)
+ }
+ p.expect(Bracket, ")")
+
+ return arguments
+}
+
+func (p *parser) parseClosure() Node {
+ startToken := p.current
+ expectClosingBracket := false
+ if p.current.Is(Bracket, "{") {
+ p.next()
+ expectClosingBracket = true
+ }
+
+ p.depth++
+ node := p.parseExpression(0)
+ p.depth--
+
+ if expectClosingBracket {
+ p.expect(Bracket, "}")
+ }
+ closure := &ClosureNode{
+ Node: node,
+ }
+ closure.SetLocation(startToken.Location)
+ return closure
+}
+
+func (p *parser) parseArrayExpression(token Token) Node {
+ nodes := make([]Node, 0)
+
+ p.expect(Bracket, "[")
+ for !p.current.Is(Bracket, "]") && p.err == nil {
+ if len(nodes) > 0 {
+ p.expect(Operator, ",")
+ if p.current.Is(Bracket, "]") {
+ goto end
+ }
+ }
+ node := p.parseExpression(0)
+ nodes = append(nodes, node)
+ }
+end:
+ p.expect(Bracket, "]")
+
+ node := &ArrayNode{Nodes: nodes}
+ node.SetLocation(token.Location)
+ return node
+}
+
+func (p *parser) parseMapExpression(token Token) Node {
+ p.expect(Bracket, "{")
+
+ nodes := make([]Node, 0)
+ for !p.current.Is(Bracket, "}") && p.err == nil {
+ if len(nodes) > 0 {
+ p.expect(Operator, ",")
+ if p.current.Is(Bracket, "}") {
+ goto end
+ }
+ if p.current.Is(Operator, ",") {
+ p.error("unexpected token %v", p.current)
+ }
+ }
+
+ var key Node
+ // Map key can be one of:
+ // * number
+ // * string
+ // * identifier, which is equivalent to a string
+ // * expression, which must be enclosed in parentheses -- (1 + 2)
+ if p.current.Is(Number) || p.current.Is(String) || p.current.Is(Identifier) {
+ key = &StringNode{Value: p.current.Value}
+ key.SetLocation(token.Location)
+ p.next()
+ } else if p.current.Is(Bracket, "(") {
+ key = p.parseExpression(0)
+ } else {
+ p.error("a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token %v)", p.current)
+ }
+
+ p.expect(Operator, ":")
+
+ node := p.parseExpression(0)
+ pair := &PairNode{Key: key, Value: node}
+ pair.SetLocation(token.Location)
+ nodes = append(nodes, pair)
+ }
+
+end:
+ p.expect(Bracket, "}")
+
+ node := &MapNode{Pairs: nodes}
+ node.SetLocation(token.Location)
+ return node
+}
+
+func (p *parser) parsePostfixExpression(node Node) Node {
+ postfixToken := p.current
+ for (postfixToken.Is(Operator) || postfixToken.Is(Bracket)) && p.err == nil {
+ optional := postfixToken.Value == "?."
+ parseToken:
+ if postfixToken.Value == "." || postfixToken.Value == "?." {
+ p.next()
+
+ propertyToken := p.current
+ if optional && propertyToken.Is(Bracket, "[") {
+ postfixToken = propertyToken
+ goto parseToken
+ }
+ p.next()
+
+ if propertyToken.Kind != Identifier &&
+ // Operators like "not" and "matches" are valid methods or property names.
+ (propertyToken.Kind != Operator || !utils.IsValidIdentifier(propertyToken.Value)) {
+ p.error("expected name")
+ }
+
+ property := &StringNode{Value: propertyToken.Value}
+ property.SetLocation(propertyToken.Location)
+
+ chainNode, isChain := node.(*ChainNode)
+ optional := postfixToken.Value == "?."
+
+ if isChain {
+ node = chainNode.Node
+ }
+
+ memberNode := &MemberNode{
+ Node: node,
+ Property: property,
+ Optional: optional,
+ }
+ memberNode.SetLocation(propertyToken.Location)
+
+ if p.current.Is(Bracket, "(") {
+ memberNode.Method = true
+ node = &CallNode{
+ Callee: memberNode,
+ Arguments: p.parseArguments([]Node{}),
+ }
+ node.SetLocation(propertyToken.Location)
+ } else {
+ node = memberNode
+ }
+
+ if isChain || optional {
+ node = &ChainNode{Node: node}
+ }
+
+ } else if postfixToken.Value == "[" {
+ p.next()
+ var from, to Node
+
+ if p.current.Is(Operator, ":") { // slice without from [:1]
+ p.next()
+
+ if !p.current.Is(Bracket, "]") { // slice without from and to [:]
+ to = p.parseExpression(0)
+ }
+
+ node = &SliceNode{
+ Node: node,
+ To: to,
+ }
+ node.SetLocation(postfixToken.Location)
+ p.expect(Bracket, "]")
+
+ } else {
+
+ from = p.parseExpression(0)
+
+ if p.current.Is(Operator, ":") {
+ p.next()
+
+ if !p.current.Is(Bracket, "]") { // slice without to [1:]
+ to = p.parseExpression(0)
+ }
+
+ node = &SliceNode{
+ Node: node,
+ From: from,
+ To: to,
+ }
+ node.SetLocation(postfixToken.Location)
+ p.expect(Bracket, "]")
+
+ } else {
+ // Slice operator [:] was not found,
+ // it should be just an index node.
+ node = &MemberNode{
+ Node: node,
+ Property: from,
+ Optional: optional,
+ }
+ node.SetLocation(postfixToken.Location)
+ if optional {
+ node = &ChainNode{Node: node}
+ }
+ p.expect(Bracket, "]")
+ }
+ }
+ } else {
+ break
+ }
+ postfixToken = p.current
+ }
+ return node
+}
+
+func (p *parser) parseComparison(left Node, token Token, precedence int) Node {
+ var rootNode Node
+ for {
+ comparator := p.parseExpression(precedence + 1)
+ cmpNode := &BinaryNode{
+ Operator: token.Value,
+ Left: left,
+ Right: comparator,
+ }
+ cmpNode.SetLocation(token.Location)
+ if rootNode == nil {
+ rootNode = cmpNode
+ } else {
+ rootNode = &BinaryNode{
+ Operator: "&&",
+ Left: rootNode,
+ Right: cmpNode,
+ }
+ rootNode.SetLocation(token.Location)
+ }
+
+ left = comparator
+ token = p.current
+ if !(token.Is(Operator) && operator.IsComparison(token.Value) && p.err == nil) {
+ break
+ }
+ p.next()
+ }
+ return rootNode
+}
diff --git a/vendor/github.com/expr-lang/expr/parser/utils/utils.go b/vendor/github.com/expr-lang/expr/parser/utils/utils.go
new file mode 100644
index 00000000..947f9a40
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/parser/utils/utils.go
@@ -0,0 +1,34 @@
+package utils
+
+import (
+ "unicode"
+ "unicode/utf8"
+)
+
+func IsValidIdentifier(str string) bool {
+ if len(str) == 0 {
+ return false
+ }
+ h, w := utf8.DecodeRuneInString(str)
+ if !IsAlphabetic(h) {
+ return false
+ }
+ for _, r := range str[w:] {
+ if !IsAlphaNumeric(r) {
+ return false
+ }
+ }
+ return true
+}
+
+func IsSpace(r rune) bool {
+ return unicode.IsSpace(r)
+}
+
+func IsAlphaNumeric(r rune) bool {
+ return IsAlphabetic(r) || unicode.IsDigit(r)
+}
+
+func IsAlphabetic(r rune) bool {
+ return r == '_' || r == '$' || unicode.IsLetter(r)
+}
diff --git a/vendor/github.com/expr-lang/expr/patcher/operator_override.go b/vendor/github.com/expr-lang/expr/patcher/operator_override.go
new file mode 100644
index 00000000..551fe09b
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/patcher/operator_override.go
@@ -0,0 +1,141 @@
+package patcher
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/conf"
+)
+
+type OperatorOverloading struct {
+ Operator string // Operator token to overload.
+ Overloads []string // List of function names to replace operator with.
+ Types conf.TypesTable // Env types.
+ Functions conf.FunctionsTable // Env functions.
+ applied bool // Flag to indicate if any changes were made to the tree.
+}
+
+func (p *OperatorOverloading) Visit(node *ast.Node) {
+ binaryNode, ok := (*node).(*ast.BinaryNode)
+ if !ok {
+ return
+ }
+
+ if binaryNode.Operator != p.Operator {
+ return
+ }
+
+ leftType := binaryNode.Left.Type()
+ rightType := binaryNode.Right.Type()
+
+ ret, fn, ok := p.FindSuitableOperatorOverload(leftType, rightType)
+ if ok {
+ newNode := &ast.CallNode{
+ Callee: &ast.IdentifierNode{Value: fn},
+ Arguments: []ast.Node{binaryNode.Left, binaryNode.Right},
+ }
+ newNode.SetType(ret)
+ ast.Patch(node, newNode)
+ p.applied = true
+ }
+}
+
+func (p *OperatorOverloading) ShouldRepeat() bool {
+ return p.applied
+}
+
+func (p *OperatorOverloading) FindSuitableOperatorOverload(l, r reflect.Type) (reflect.Type, string, bool) {
+ t, fn, ok := p.findSuitableOperatorOverloadInFunctions(l, r)
+ if !ok {
+ t, fn, ok = p.findSuitableOperatorOverloadInTypes(l, r)
+ }
+ return t, fn, ok
+}
+
+func (p *OperatorOverloading) findSuitableOperatorOverloadInTypes(l, r reflect.Type) (reflect.Type, string, bool) {
+ for _, fn := range p.Overloads {
+ fnType, ok := p.Types[fn]
+ if !ok {
+ continue
+ }
+ firstInIndex := 0
+ if fnType.Method {
+ firstInIndex = 1 // As first argument to method is receiver.
+ }
+ ret, done := checkTypeSuits(fnType.Type, l, r, firstInIndex)
+ if done {
+ return ret, fn, true
+ }
+ }
+ return nil, "", false
+}
+
+func (p *OperatorOverloading) findSuitableOperatorOverloadInFunctions(l, r reflect.Type) (reflect.Type, string, bool) {
+ for _, fn := range p.Overloads {
+ fnType, ok := p.Functions[fn]
+ if !ok {
+ continue
+ }
+ firstInIndex := 0
+ for _, overload := range fnType.Types {
+ ret, done := checkTypeSuits(overload, l, r, firstInIndex)
+ if done {
+ return ret, fn, true
+ }
+ }
+ }
+ return nil, "", false
+}
+
+func checkTypeSuits(t reflect.Type, l reflect.Type, r reflect.Type, firstInIndex int) (reflect.Type, bool) {
+ firstArgType := t.In(firstInIndex)
+ secondArgType := t.In(firstInIndex + 1)
+
+ firstArgumentFit := l == firstArgType || (firstArgType.Kind() == reflect.Interface && (l == nil || l.Implements(firstArgType)))
+ secondArgumentFit := r == secondArgType || (secondArgType.Kind() == reflect.Interface && (r == nil || r.Implements(secondArgType)))
+ if firstArgumentFit && secondArgumentFit {
+ return t.Out(0), true
+ }
+ return nil, false
+}
+
+func (p *OperatorOverloading) Check() {
+ for _, fn := range p.Overloads {
+ fnType, foundType := p.Types[fn]
+ fnFunc, foundFunc := p.Functions[fn]
+ if !foundFunc && (!foundType || fnType.Type.Kind() != reflect.Func) {
+ panic(fmt.Errorf("function %s for %s operator does not exist in the environment", fn, p.Operator))
+ }
+
+ if foundType {
+ checkType(fnType, fn, p.Operator)
+ }
+
+ if foundFunc {
+ checkFunc(fnFunc, fn, p.Operator)
+ }
+ }
+}
+
+func checkType(fnType conf.Tag, fn string, operator string) {
+ requiredNumIn := 2
+ if fnType.Method {
+ requiredNumIn = 3 // As first argument of method is receiver.
+ }
+ if fnType.Type.NumIn() != requiredNumIn || fnType.Type.NumOut() != 1 {
+ panic(fmt.Errorf("function %s for %s operator does not have a correct signature", fn, operator))
+ }
+}
+
+func checkFunc(fn *builtin.Function, name string, operator string) {
+ if len(fn.Types) == 0 {
+ panic(fmt.Errorf("function %q for %q operator misses types", name, operator))
+ }
+ for _, t := range fn.Types {
+ if t.NumIn() != 2 || t.NumOut() != 1 {
+ panic(fmt.Errorf("function %q for %q operator does not have a correct signature", name, operator))
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/patcher/with_context.go b/vendor/github.com/expr-lang/expr/patcher/with_context.go
new file mode 100644
index 00000000..f9861a2c
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/patcher/with_context.go
@@ -0,0 +1,45 @@
+package patcher
+
+import (
+ "reflect"
+
+ "github.com/expr-lang/expr/ast"
+)
+
+// WithContext adds WithContext.Name argument to all functions calls with a context.Context argument.
+type WithContext struct {
+ Name string
+}
+
+// Visit adds WithContext.Name argument to all functions calls with a context.Context argument.
+func (w WithContext) Visit(node *ast.Node) {
+ switch call := (*node).(type) {
+ case *ast.CallNode:
+ fn := call.Callee.Type()
+ if fn == nil {
+ return
+ }
+ if fn.Kind() != reflect.Func {
+ return
+ }
+ switch fn.NumIn() {
+ case 0:
+ return
+ case 1:
+ if fn.In(0).String() != "context.Context" {
+ return
+ }
+ default:
+ if fn.In(0).String() != "context.Context" &&
+ fn.In(1).String() != "context.Context" {
+ return
+ }
+ }
+ ast.Patch(node, &ast.CallNode{
+ Callee: call.Callee,
+ Arguments: append([]ast.Node{
+ &ast.IdentifierNode{Value: w.Name},
+ }, call.Arguments...),
+ })
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/patcher/with_timezone.go b/vendor/github.com/expr-lang/expr/patcher/with_timezone.go
new file mode 100644
index 00000000..83eb28e9
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/patcher/with_timezone.go
@@ -0,0 +1,25 @@
+package patcher
+
+import (
+ "time"
+
+ "github.com/expr-lang/expr/ast"
+)
+
+// WithTimezone passes Location to date() and now() functions.
+type WithTimezone struct {
+ Location *time.Location
+}
+
+func (t WithTimezone) Visit(node *ast.Node) {
+ if btin, ok := (*node).(*ast.BuiltinNode); ok {
+ switch btin.Name {
+ case "date", "now":
+ loc := &ast.ConstantNode{Value: t.Location}
+ ast.Patch(node, &ast.BuiltinNode{
+ Name: btin.Name,
+ Arguments: append([]ast.Node{loc}, btin.Arguments...),
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/vm/debug.go b/vendor/github.com/expr-lang/expr/vm/debug.go
new file mode 100644
index 00000000..ab95bf9a
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/debug.go
@@ -0,0 +1,5 @@
+//go:build expr_debug
+
+package vm
+
+const debug = true
diff --git a/vendor/github.com/expr-lang/expr/vm/debug_off.go b/vendor/github.com/expr-lang/expr/vm/debug_off.go
new file mode 100644
index 00000000..e0f2955a
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/debug_off.go
@@ -0,0 +1,5 @@
+//go:build !expr_debug
+
+package vm
+
+const debug = false
diff --git a/vendor/github.com/expr-lang/expr/vm/func_types[generated].go b/vendor/github.com/expr-lang/expr/vm/func_types[generated].go
new file mode 100644
index 00000000..610b4152
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/func_types[generated].go
@@ -0,0 +1,370 @@
+// Code generated by vm/func_types/main.go. DO NOT EDIT.
+
+package vm
+
+import (
+ "fmt"
+ "time"
+)
+
+var FuncTypes = []any{
+ 1: new(func() time.Duration),
+ 2: new(func() time.Month),
+ 3: new(func() time.Time),
+ 4: new(func() time.Weekday),
+ 5: new(func() []interface{}),
+ 6: new(func() []uint8),
+ 7: new(func() interface{}),
+ 8: new(func() bool),
+ 9: new(func() uint8),
+ 10: new(func() float32),
+ 11: new(func() float64),
+ 12: new(func() int),
+ 13: new(func() int16),
+ 14: new(func() int32),
+ 15: new(func() int64),
+ 16: new(func() int8),
+ 17: new(func() map[string]interface{}),
+ 18: new(func() int32),
+ 19: new(func() string),
+ 20: new(func() uint),
+ 21: new(func() uint16),
+ 22: new(func() uint32),
+ 23: new(func() uint64),
+ 24: new(func() uint8),
+ 25: new(func(time.Duration) time.Duration),
+ 26: new(func(time.Duration) time.Time),
+ 27: new(func(time.Time) time.Duration),
+ 28: new(func(time.Time) bool),
+ 29: new(func([]interface{}) []interface{}),
+ 30: new(func([]interface{}) interface{}),
+ 31: new(func([]interface{}) map[string]interface{}),
+ 32: new(func([]interface{}, string) string),
+ 33: new(func([]uint8) string),
+ 34: new(func([]string, string) string),
+ 35: new(func(interface{}) []interface{}),
+ 36: new(func(interface{}) interface{}),
+ 37: new(func(interface{}) bool),
+ 38: new(func(interface{}) float64),
+ 39: new(func(interface{}) int),
+ 40: new(func(interface{}) map[string]interface{}),
+ 41: new(func(interface{}) string),
+ 42: new(func(interface{}, interface{}) []interface{}),
+ 43: new(func(interface{}, interface{}) interface{}),
+ 44: new(func(interface{}, interface{}) bool),
+ 45: new(func(interface{}, interface{}) string),
+ 46: new(func(bool) bool),
+ 47: new(func(bool) float64),
+ 48: new(func(bool) int),
+ 49: new(func(bool) string),
+ 50: new(func(bool, bool) bool),
+ 51: new(func(float32) float64),
+ 52: new(func(float64) bool),
+ 53: new(func(float64) float32),
+ 54: new(func(float64) float64),
+ 55: new(func(float64) int),
+ 56: new(func(float64) string),
+ 57: new(func(float64, float64) bool),
+ 58: new(func(int) bool),
+ 59: new(func(int) float64),
+ 60: new(func(int) int),
+ 61: new(func(int) string),
+ 62: new(func(int, int) bool),
+ 63: new(func(int, int) int),
+ 64: new(func(int, int) string),
+ 65: new(func(int16) int32),
+ 66: new(func(int32) float64),
+ 67: new(func(int32) int),
+ 68: new(func(int32) int64),
+ 69: new(func(int64) time.Time),
+ 70: new(func(int8) int),
+ 71: new(func(int8) int16),
+ 72: new(func(string) []uint8),
+ 73: new(func(string) []string),
+ 74: new(func(string) bool),
+ 75: new(func(string) float64),
+ 76: new(func(string) int),
+ 77: new(func(string) string),
+ 78: new(func(string, uint8) int),
+ 79: new(func(string, int) int),
+ 80: new(func(string, int32) int),
+ 81: new(func(string, string) bool),
+ 82: new(func(string, string) string),
+ 83: new(func(uint) float64),
+ 84: new(func(uint) int),
+ 85: new(func(uint) uint),
+ 86: new(func(uint16) uint),
+ 87: new(func(uint32) uint64),
+ 88: new(func(uint64) float64),
+ 89: new(func(uint64) int64),
+ 90: new(func(uint8) uint8),
+}
+
+func (vm *VM) call(fn any, kind int) any {
+ switch kind {
+ case 1:
+ return fn.(func() time.Duration)()
+ case 2:
+ return fn.(func() time.Month)()
+ case 3:
+ return fn.(func() time.Time)()
+ case 4:
+ return fn.(func() time.Weekday)()
+ case 5:
+ return fn.(func() []interface{})()
+ case 6:
+ return fn.(func() []uint8)()
+ case 7:
+ return fn.(func() interface{})()
+ case 8:
+ return fn.(func() bool)()
+ case 9:
+ return fn.(func() uint8)()
+ case 10:
+ return fn.(func() float32)()
+ case 11:
+ return fn.(func() float64)()
+ case 12:
+ return fn.(func() int)()
+ case 13:
+ return fn.(func() int16)()
+ case 14:
+ return fn.(func() int32)()
+ case 15:
+ return fn.(func() int64)()
+ case 16:
+ return fn.(func() int8)()
+ case 17:
+ return fn.(func() map[string]interface{})()
+ case 18:
+ return fn.(func() int32)()
+ case 19:
+ return fn.(func() string)()
+ case 20:
+ return fn.(func() uint)()
+ case 21:
+ return fn.(func() uint16)()
+ case 22:
+ return fn.(func() uint32)()
+ case 23:
+ return fn.(func() uint64)()
+ case 24:
+ return fn.(func() uint8)()
+ case 25:
+ arg1 := vm.pop().(time.Duration)
+ return fn.(func(time.Duration) time.Duration)(arg1)
+ case 26:
+ arg1 := vm.pop().(time.Duration)
+ return fn.(func(time.Duration) time.Time)(arg1)
+ case 27:
+ arg1 := vm.pop().(time.Time)
+ return fn.(func(time.Time) time.Duration)(arg1)
+ case 28:
+ arg1 := vm.pop().(time.Time)
+ return fn.(func(time.Time) bool)(arg1)
+ case 29:
+ arg1 := vm.pop().([]interface{})
+ return fn.(func([]interface{}) []interface{})(arg1)
+ case 30:
+ arg1 := vm.pop().([]interface{})
+ return fn.(func([]interface{}) interface{})(arg1)
+ case 31:
+ arg1 := vm.pop().([]interface{})
+ return fn.(func([]interface{}) map[string]interface{})(arg1)
+ case 32:
+ arg2 := vm.pop().(string)
+ arg1 := vm.pop().([]interface{})
+ return fn.(func([]interface{}, string) string)(arg1, arg2)
+ case 33:
+ arg1 := vm.pop().([]uint8)
+ return fn.(func([]uint8) string)(arg1)
+ case 34:
+ arg2 := vm.pop().(string)
+ arg1 := vm.pop().([]string)
+ return fn.(func([]string, string) string)(arg1, arg2)
+ case 35:
+ arg1 := vm.pop()
+ return fn.(func(interface{}) []interface{})(arg1)
+ case 36:
+ arg1 := vm.pop()
+ return fn.(func(interface{}) interface{})(arg1)
+ case 37:
+ arg1 := vm.pop()
+ return fn.(func(interface{}) bool)(arg1)
+ case 38:
+ arg1 := vm.pop()
+ return fn.(func(interface{}) float64)(arg1)
+ case 39:
+ arg1 := vm.pop()
+ return fn.(func(interface{}) int)(arg1)
+ case 40:
+ arg1 := vm.pop()
+ return fn.(func(interface{}) map[string]interface{})(arg1)
+ case 41:
+ arg1 := vm.pop()
+ return fn.(func(interface{}) string)(arg1)
+ case 42:
+ arg2 := vm.pop()
+ arg1 := vm.pop()
+ return fn.(func(interface{}, interface{}) []interface{})(arg1, arg2)
+ case 43:
+ arg2 := vm.pop()
+ arg1 := vm.pop()
+ return fn.(func(interface{}, interface{}) interface{})(arg1, arg2)
+ case 44:
+ arg2 := vm.pop()
+ arg1 := vm.pop()
+ return fn.(func(interface{}, interface{}) bool)(arg1, arg2)
+ case 45:
+ arg2 := vm.pop()
+ arg1 := vm.pop()
+ return fn.(func(interface{}, interface{}) string)(arg1, arg2)
+ case 46:
+ arg1 := vm.pop().(bool)
+ return fn.(func(bool) bool)(arg1)
+ case 47:
+ arg1 := vm.pop().(bool)
+ return fn.(func(bool) float64)(arg1)
+ case 48:
+ arg1 := vm.pop().(bool)
+ return fn.(func(bool) int)(arg1)
+ case 49:
+ arg1 := vm.pop().(bool)
+ return fn.(func(bool) string)(arg1)
+ case 50:
+ arg2 := vm.pop().(bool)
+ arg1 := vm.pop().(bool)
+ return fn.(func(bool, bool) bool)(arg1, arg2)
+ case 51:
+ arg1 := vm.pop().(float32)
+ return fn.(func(float32) float64)(arg1)
+ case 52:
+ arg1 := vm.pop().(float64)
+ return fn.(func(float64) bool)(arg1)
+ case 53:
+ arg1 := vm.pop().(float64)
+ return fn.(func(float64) float32)(arg1)
+ case 54:
+ arg1 := vm.pop().(float64)
+ return fn.(func(float64) float64)(arg1)
+ case 55:
+ arg1 := vm.pop().(float64)
+ return fn.(func(float64) int)(arg1)
+ case 56:
+ arg1 := vm.pop().(float64)
+ return fn.(func(float64) string)(arg1)
+ case 57:
+ arg2 := vm.pop().(float64)
+ arg1 := vm.pop().(float64)
+ return fn.(func(float64, float64) bool)(arg1, arg2)
+ case 58:
+ arg1 := vm.pop().(int)
+ return fn.(func(int) bool)(arg1)
+ case 59:
+ arg1 := vm.pop().(int)
+ return fn.(func(int) float64)(arg1)
+ case 60:
+ arg1 := vm.pop().(int)
+ return fn.(func(int) int)(arg1)
+ case 61:
+ arg1 := vm.pop().(int)
+ return fn.(func(int) string)(arg1)
+ case 62:
+ arg2 := vm.pop().(int)
+ arg1 := vm.pop().(int)
+ return fn.(func(int, int) bool)(arg1, arg2)
+ case 63:
+ arg2 := vm.pop().(int)
+ arg1 := vm.pop().(int)
+ return fn.(func(int, int) int)(arg1, arg2)
+ case 64:
+ arg2 := vm.pop().(int)
+ arg1 := vm.pop().(int)
+ return fn.(func(int, int) string)(arg1, arg2)
+ case 65:
+ arg1 := vm.pop().(int16)
+ return fn.(func(int16) int32)(arg1)
+ case 66:
+ arg1 := vm.pop().(int32)
+ return fn.(func(int32) float64)(arg1)
+ case 67:
+ arg1 := vm.pop().(int32)
+ return fn.(func(int32) int)(arg1)
+ case 68:
+ arg1 := vm.pop().(int32)
+ return fn.(func(int32) int64)(arg1)
+ case 69:
+ arg1 := vm.pop().(int64)
+ return fn.(func(int64) time.Time)(arg1)
+ case 70:
+ arg1 := vm.pop().(int8)
+ return fn.(func(int8) int)(arg1)
+ case 71:
+ arg1 := vm.pop().(int8)
+ return fn.(func(int8) int16)(arg1)
+ case 72:
+ arg1 := vm.pop().(string)
+ return fn.(func(string) []uint8)(arg1)
+ case 73:
+ arg1 := vm.pop().(string)
+ return fn.(func(string) []string)(arg1)
+ case 74:
+ arg1 := vm.pop().(string)
+ return fn.(func(string) bool)(arg1)
+ case 75:
+ arg1 := vm.pop().(string)
+ return fn.(func(string) float64)(arg1)
+ case 76:
+ arg1 := vm.pop().(string)
+ return fn.(func(string) int)(arg1)
+ case 77:
+ arg1 := vm.pop().(string)
+ return fn.(func(string) string)(arg1)
+ case 78:
+ arg2 := vm.pop().(uint8)
+ arg1 := vm.pop().(string)
+ return fn.(func(string, uint8) int)(arg1, arg2)
+ case 79:
+ arg2 := vm.pop().(int)
+ arg1 := vm.pop().(string)
+ return fn.(func(string, int) int)(arg1, arg2)
+ case 80:
+ arg2 := vm.pop().(int32)
+ arg1 := vm.pop().(string)
+ return fn.(func(string, int32) int)(arg1, arg2)
+ case 81:
+ arg2 := vm.pop().(string)
+ arg1 := vm.pop().(string)
+ return fn.(func(string, string) bool)(arg1, arg2)
+ case 82:
+ arg2 := vm.pop().(string)
+ arg1 := vm.pop().(string)
+ return fn.(func(string, string) string)(arg1, arg2)
+ case 83:
+ arg1 := vm.pop().(uint)
+ return fn.(func(uint) float64)(arg1)
+ case 84:
+ arg1 := vm.pop().(uint)
+ return fn.(func(uint) int)(arg1)
+ case 85:
+ arg1 := vm.pop().(uint)
+ return fn.(func(uint) uint)(arg1)
+ case 86:
+ arg1 := vm.pop().(uint16)
+ return fn.(func(uint16) uint)(arg1)
+ case 87:
+ arg1 := vm.pop().(uint32)
+ return fn.(func(uint32) uint64)(arg1)
+ case 88:
+ arg1 := vm.pop().(uint64)
+ return fn.(func(uint64) float64)(arg1)
+ case 89:
+ arg1 := vm.pop().(uint64)
+ return fn.(func(uint64) int64)(arg1)
+ case 90:
+ arg1 := vm.pop().(uint8)
+ return fn.(func(uint8) uint8)(arg1)
+
+ }
+ panic(fmt.Sprintf("unknown function kind (%v)", kind))
+}
diff --git a/vendor/github.com/expr-lang/expr/vm/opcodes.go b/vendor/github.com/expr-lang/expr/vm/opcodes.go
new file mode 100644
index 00000000..84d751d6
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/opcodes.go
@@ -0,0 +1,88 @@
+package vm
+
+type Opcode byte
+
+const (
+ OpInvalid Opcode = iota
+ OpPush
+ OpInt
+ OpPop
+ OpStore
+ OpLoadVar
+ OpLoadConst
+ OpLoadField
+ OpLoadFast
+ OpLoadMethod
+ OpLoadFunc
+ OpLoadEnv
+ OpFetch
+ OpFetchField
+ OpMethod
+ OpTrue
+ OpFalse
+ OpNil
+ OpNegate
+ OpNot
+ OpEqual
+ OpEqualInt
+ OpEqualString
+ OpJump
+ OpJumpIfTrue
+ OpJumpIfFalse
+ OpJumpIfNil
+ OpJumpIfNotNil
+ OpJumpIfEnd
+ OpJumpBackward
+ OpIn
+ OpLess
+ OpMore
+ OpLessOrEqual
+ OpMoreOrEqual
+ OpAdd
+ OpSubtract
+ OpMultiply
+ OpDivide
+ OpModulo
+ OpExponent
+ OpRange
+ OpMatches
+ OpMatchesConst
+ OpContains
+ OpStartsWith
+ OpEndsWith
+ OpSlice
+ OpCall
+ OpCall0
+ OpCall1
+ OpCall2
+ OpCall3
+ OpCallN
+ OpCallFast
+ OpCallSafe
+ OpCallTyped
+ OpCallBuiltin1
+ OpArray
+ OpMap
+ OpLen
+ OpCast
+ OpDeref
+ OpIncrementIndex
+ OpDecrementIndex
+ OpIncrementCount
+ OpGetIndex
+ OpGetCount
+ OpGetLen
+ OpGetAcc
+ OpSetAcc
+ OpSetIndex
+ OpPointer
+ OpThrow
+ OpCreate
+ OpGroupBy
+ OpSortBy
+ OpSort
+ OpProfileStart
+ OpProfileEnd
+ OpBegin
+ OpEnd // This opcode must be at the end of this list.
+)
diff --git a/vendor/github.com/expr-lang/expr/vm/program.go b/vendor/github.com/expr-lang/expr/vm/program.go
new file mode 100644
index 00000000..15ce26f5
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/program.go
@@ -0,0 +1,382 @@
+package vm
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "regexp"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/expr-lang/expr/ast"
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/file"
+ "github.com/expr-lang/expr/vm/runtime"
+)
+
+// Program represents a compiled expression.
+type Program struct {
+ Bytecode []Opcode
+ Arguments []int
+ Constants []any
+
+ source file.Source
+ node ast.Node
+ locations []file.Location
+ variables int
+ functions []Function
+ debugInfo map[string]string
+ span *Span
+}
+
+// NewProgram returns a new Program. It's used by the compiler.
+func NewProgram(
+ source file.Source,
+ node ast.Node,
+ locations []file.Location,
+ variables int,
+ constants []any,
+ bytecode []Opcode,
+ arguments []int,
+ functions []Function,
+ debugInfo map[string]string,
+ span *Span,
+) *Program {
+ return &Program{
+ source: source,
+ node: node,
+ locations: locations,
+ variables: variables,
+ Constants: constants,
+ Bytecode: bytecode,
+ Arguments: arguments,
+ functions: functions,
+ debugInfo: debugInfo,
+ span: span,
+ }
+}
+
+// Source returns origin file.Source.
+func (program *Program) Source() file.Source {
+ return program.source
+}
+
+// Node returns origin ast.Node.
+func (program *Program) Node() ast.Node {
+ return program.node
+}
+
+// Locations returns a slice of bytecode's locations.
+func (program *Program) Locations() []file.Location {
+ return program.locations
+}
+
+// Disassemble returns opcodes as a string.
+func (program *Program) Disassemble() string {
+ var buf bytes.Buffer
+ w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
+ program.DisassembleWriter(w)
+ _ = w.Flush()
+ return buf.String()
+}
+
+// DisassembleWriter takes a writer and writes opcodes to it.
+func (program *Program) DisassembleWriter(w io.Writer) {
+ ip := 0
+ for ip < len(program.Bytecode) {
+ pp := ip
+ op := program.Bytecode[ip]
+ arg := program.Arguments[ip]
+ ip += 1
+
+ code := func(label string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\n", pp, label)
+ }
+ jump := func(label string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t(%v)\n", pp, label, arg, ip+arg)
+ }
+ jumpBack := func(label string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t(%v)\n", pp, label, arg, ip-arg)
+ }
+ argument := func(label string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\n", pp, label, arg)
+ }
+ argumentWithInfo := func(label string, prefix string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, program.debugInfo[fmt.Sprintf("%s_%d", prefix, arg)])
+ }
+ constant := func(label string) {
+ var c any
+ if arg < len(program.Constants) {
+ c = program.Constants[arg]
+ } else {
+ c = "out of range"
+ }
+ if r, ok := c.(*regexp.Regexp); ok {
+ c = r.String()
+ }
+ if field, ok := c.(*runtime.Field); ok {
+ c = fmt.Sprintf("{%v %v}", strings.Join(field.Path, "."), field.Index)
+ }
+ if method, ok := c.(*runtime.Method); ok {
+ c = fmt.Sprintf("{%v %v}", method.Name, method.Index)
+ }
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, c)
+ }
+ builtinArg := func(label string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, builtin.Builtins[arg].Name)
+ }
+
+ switch op {
+ case OpInvalid:
+ code("OpInvalid")
+
+ case OpPush:
+ constant("OpPush")
+
+ case OpInt:
+ argument("OpInt")
+
+ case OpPop:
+ code("OpPop")
+
+ case OpStore:
+ argumentWithInfo("OpStore", "var")
+
+ case OpLoadVar:
+ argumentWithInfo("OpLoadVar", "var")
+
+ case OpLoadConst:
+ constant("OpLoadConst")
+
+ case OpLoadField:
+ constant("OpLoadField")
+
+ case OpLoadFast:
+ constant("OpLoadFast")
+
+ case OpLoadMethod:
+ constant("OpLoadMethod")
+
+ case OpLoadFunc:
+ argumentWithInfo("OpLoadFunc", "func")
+
+ case OpLoadEnv:
+ code("OpLoadEnv")
+
+ case OpFetch:
+ code("OpFetch")
+
+ case OpFetchField:
+ constant("OpFetchField")
+
+ case OpMethod:
+ constant("OpMethod")
+
+ case OpTrue:
+ code("OpTrue")
+
+ case OpFalse:
+ code("OpFalse")
+
+ case OpNil:
+ code("OpNil")
+
+ case OpNegate:
+ code("OpNegate")
+
+ case OpNot:
+ code("OpNot")
+
+ case OpEqual:
+ code("OpEqual")
+
+ case OpEqualInt:
+ code("OpEqualInt")
+
+ case OpEqualString:
+ code("OpEqualString")
+
+ case OpJump:
+ jump("OpJump")
+
+ case OpJumpIfTrue:
+ jump("OpJumpIfTrue")
+
+ case OpJumpIfFalse:
+ jump("OpJumpIfFalse")
+
+ case OpJumpIfNil:
+ jump("OpJumpIfNil")
+
+ case OpJumpIfNotNil:
+ jump("OpJumpIfNotNil")
+
+ case OpJumpIfEnd:
+ jump("OpJumpIfEnd")
+
+ case OpJumpBackward:
+ jumpBack("OpJumpBackward")
+
+ case OpIn:
+ code("OpIn")
+
+ case OpLess:
+ code("OpLess")
+
+ case OpMore:
+ code("OpMore")
+
+ case OpLessOrEqual:
+ code("OpLessOrEqual")
+
+ case OpMoreOrEqual:
+ code("OpMoreOrEqual")
+
+ case OpAdd:
+ code("OpAdd")
+
+ case OpSubtract:
+ code("OpSubtract")
+
+ case OpMultiply:
+ code("OpMultiply")
+
+ case OpDivide:
+ code("OpDivide")
+
+ case OpModulo:
+ code("OpModulo")
+
+ case OpExponent:
+ code("OpExponent")
+
+ case OpRange:
+ code("OpRange")
+
+ case OpMatches:
+ code("OpMatches")
+
+ case OpMatchesConst:
+ constant("OpMatchesConst")
+
+ case OpContains:
+ code("OpContains")
+
+ case OpStartsWith:
+ code("OpStartsWith")
+
+ case OpEndsWith:
+ code("OpEndsWith")
+
+ case OpSlice:
+ code("OpSlice")
+
+ case OpCall:
+ argument("OpCall")
+
+ case OpCall0:
+ argumentWithInfo("OpCall0", "func")
+
+ case OpCall1:
+ argumentWithInfo("OpCall1", "func")
+
+ case OpCall2:
+ argumentWithInfo("OpCall2", "func")
+
+ case OpCall3:
+ argumentWithInfo("OpCall3", "func")
+
+ case OpCallN:
+ argument("OpCallN")
+
+ case OpCallFast:
+ argument("OpCallFast")
+
+ case OpCallSafe:
+ argument("OpCallSafe")
+
+ case OpCallTyped:
+ signature := reflect.TypeOf(FuncTypes[arg]).Elem().String()
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, "OpCallTyped", arg, signature)
+
+ case OpCallBuiltin1:
+ builtinArg("OpCallBuiltin1")
+
+ case OpArray:
+ code("OpArray")
+
+ case OpMap:
+ code("OpMap")
+
+ case OpLen:
+ code("OpLen")
+
+ case OpCast:
+ argument("OpCast")
+
+ case OpDeref:
+ code("OpDeref")
+
+ case OpIncrementIndex:
+ code("OpIncrementIndex")
+
+ case OpDecrementIndex:
+ code("OpDecrementIndex")
+
+ case OpIncrementCount:
+ code("OpIncrementCount")
+
+ case OpGetIndex:
+ code("OpGetIndex")
+
+ case OpGetCount:
+ code("OpGetCount")
+
+ case OpGetLen:
+ code("OpGetLen")
+
+ case OpGetAcc:
+ code("OpGetAcc")
+
+ case OpSetAcc:
+ code("OpSetAcc")
+
+ case OpSetIndex:
+ code("OpSetIndex")
+
+ case OpPointer:
+ code("OpPointer")
+
+ case OpThrow:
+ code("OpThrow")
+
+ case OpCreate:
+ argument("OpCreate")
+
+ case OpGroupBy:
+ code("OpGroupBy")
+
+ case OpSortBy:
+ code("OpSortBy")
+
+ case OpSort:
+ code("OpSort")
+
+ case OpProfileStart:
+ code("OpProfileStart")
+
+ case OpProfileEnd:
+ code("OpProfileEnd")
+
+ case OpBegin:
+ code("OpBegin")
+
+ case OpEnd:
+ code("OpEnd")
+
+ default:
+ _, _ = fmt.Fprintf(w, "%v\t%#x (unknown)\n", ip, op)
+ }
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/vm/runtime/helpers[generated].go b/vendor/github.com/expr-lang/expr/vm/runtime/helpers[generated].go
new file mode 100644
index 00000000..d950f111
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/runtime/helpers[generated].go
@@ -0,0 +1,3718 @@
+// Code generated by vm/runtime/helpers/main.go. DO NOT EDIT.
+
+package runtime
+
+import (
+ "fmt"
+ "reflect"
+ "time"
+)
+
+func Equal(a, b interface{}) bool {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) == int(y)
+ case uint8:
+ return int(x) == int(y)
+ case uint16:
+ return int(x) == int(y)
+ case uint32:
+ return int(x) == int(y)
+ case uint64:
+ return int(x) == int(y)
+ case int:
+ return int(x) == int(y)
+ case int8:
+ return int(x) == int(y)
+ case int16:
+ return int(x) == int(y)
+ case int32:
+ return int(x) == int(y)
+ case int64:
+ return int(x) == int(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) == float64(y)
+ case uint8:
+ return float64(x) == float64(y)
+ case uint16:
+ return float64(x) == float64(y)
+ case uint32:
+ return float64(x) == float64(y)
+ case uint64:
+ return float64(x) == float64(y)
+ case int:
+ return float64(x) == float64(y)
+ case int8:
+ return float64(x) == float64(y)
+ case int16:
+ return float64(x) == float64(y)
+ case int32:
+ return float64(x) == float64(y)
+ case int64:
+ return float64(x) == float64(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) == float64(y)
+ case uint8:
+ return float64(x) == float64(y)
+ case uint16:
+ return float64(x) == float64(y)
+ case uint32:
+ return float64(x) == float64(y)
+ case uint64:
+ return float64(x) == float64(y)
+ case int:
+ return float64(x) == float64(y)
+ case int8:
+ return float64(x) == float64(y)
+ case int16:
+ return float64(x) == float64(y)
+ case int32:
+ return float64(x) == float64(y)
+ case int64:
+ return float64(x) == float64(y)
+ case float32:
+ return float64(x) == float64(y)
+ case float64:
+ return float64(x) == float64(y)
+ }
+ case []any:
+ switch y := b.(type) {
+ case []string:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []uint:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []uint8:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []uint16:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []uint32:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []uint64:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []int:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []int8:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []int16:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []int32:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []int64:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []float32:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []float64:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ case []any:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if !Equal(x[i], y[i]) {
+ return false
+ }
+ }
+ return true
+ }
+ case []string:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []string:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []uint:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []uint:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []uint8:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []uint8:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []uint16:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []uint16:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []uint32:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []uint32:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []uint64:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []uint64:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []int:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []int:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []int8:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []int8:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []int16:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []int16:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []int32:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []int32:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []int64:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []int64:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []float32:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []float32:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case []float64:
+ switch y := b.(type) {
+ case []any:
+ return Equal(y, x)
+ case []float64:
+ if len(x) != len(y) {
+ return false
+ }
+ for i := range x {
+ if x[i] != y[i] {
+ return false
+ }
+ }
+ return true
+ }
+ case string:
+ switch y := b.(type) {
+ case string:
+ return x == y
+ }
+ case time.Time:
+ switch y := b.(type) {
+ case time.Time:
+ return x.Equal(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x == y
+ }
+ case bool:
+ switch y := b.(type) {
+ case bool:
+ return x == y
+ }
+ }
+ if IsNil(a) && IsNil(b) {
+ return true
+ }
+ return reflect.DeepEqual(a, b)
+}
+
+func Less(a, b interface{}) bool {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) < int(y)
+ case uint8:
+ return int(x) < int(y)
+ case uint16:
+ return int(x) < int(y)
+ case uint32:
+ return int(x) < int(y)
+ case uint64:
+ return int(x) < int(y)
+ case int:
+ return int(x) < int(y)
+ case int8:
+ return int(x) < int(y)
+ case int16:
+ return int(x) < int(y)
+ case int32:
+ return int(x) < int(y)
+ case int64:
+ return int(x) < int(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) < float64(y)
+ case uint8:
+ return float64(x) < float64(y)
+ case uint16:
+ return float64(x) < float64(y)
+ case uint32:
+ return float64(x) < float64(y)
+ case uint64:
+ return float64(x) < float64(y)
+ case int:
+ return float64(x) < float64(y)
+ case int8:
+ return float64(x) < float64(y)
+ case int16:
+ return float64(x) < float64(y)
+ case int32:
+ return float64(x) < float64(y)
+ case int64:
+ return float64(x) < float64(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) < float64(y)
+ case uint8:
+ return float64(x) < float64(y)
+ case uint16:
+ return float64(x) < float64(y)
+ case uint32:
+ return float64(x) < float64(y)
+ case uint64:
+ return float64(x) < float64(y)
+ case int:
+ return float64(x) < float64(y)
+ case int8:
+ return float64(x) < float64(y)
+ case int16:
+ return float64(x) < float64(y)
+ case int32:
+ return float64(x) < float64(y)
+ case int64:
+ return float64(x) < float64(y)
+ case float32:
+ return float64(x) < float64(y)
+ case float64:
+ return float64(x) < float64(y)
+ }
+ case string:
+ switch y := b.(type) {
+ case string:
+ return x < y
+ }
+ case time.Time:
+ switch y := b.(type) {
+ case time.Time:
+ return x.Before(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x < y
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T < %T", a, b))
+}
+
+func More(a, b interface{}) bool {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) > int(y)
+ case uint8:
+ return int(x) > int(y)
+ case uint16:
+ return int(x) > int(y)
+ case uint32:
+ return int(x) > int(y)
+ case uint64:
+ return int(x) > int(y)
+ case int:
+ return int(x) > int(y)
+ case int8:
+ return int(x) > int(y)
+ case int16:
+ return int(x) > int(y)
+ case int32:
+ return int(x) > int(y)
+ case int64:
+ return int(x) > int(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) > float64(y)
+ case uint8:
+ return float64(x) > float64(y)
+ case uint16:
+ return float64(x) > float64(y)
+ case uint32:
+ return float64(x) > float64(y)
+ case uint64:
+ return float64(x) > float64(y)
+ case int:
+ return float64(x) > float64(y)
+ case int8:
+ return float64(x) > float64(y)
+ case int16:
+ return float64(x) > float64(y)
+ case int32:
+ return float64(x) > float64(y)
+ case int64:
+ return float64(x) > float64(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) > float64(y)
+ case uint8:
+ return float64(x) > float64(y)
+ case uint16:
+ return float64(x) > float64(y)
+ case uint32:
+ return float64(x) > float64(y)
+ case uint64:
+ return float64(x) > float64(y)
+ case int:
+ return float64(x) > float64(y)
+ case int8:
+ return float64(x) > float64(y)
+ case int16:
+ return float64(x) > float64(y)
+ case int32:
+ return float64(x) > float64(y)
+ case int64:
+ return float64(x) > float64(y)
+ case float32:
+ return float64(x) > float64(y)
+ case float64:
+ return float64(x) > float64(y)
+ }
+ case string:
+ switch y := b.(type) {
+ case string:
+ return x > y
+ }
+ case time.Time:
+ switch y := b.(type) {
+ case time.Time:
+ return x.After(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x > y
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T > %T", a, b))
+}
+
+func LessOrEqual(a, b interface{}) bool {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) <= int(y)
+ case uint8:
+ return int(x) <= int(y)
+ case uint16:
+ return int(x) <= int(y)
+ case uint32:
+ return int(x) <= int(y)
+ case uint64:
+ return int(x) <= int(y)
+ case int:
+ return int(x) <= int(y)
+ case int8:
+ return int(x) <= int(y)
+ case int16:
+ return int(x) <= int(y)
+ case int32:
+ return int(x) <= int(y)
+ case int64:
+ return int(x) <= int(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) <= float64(y)
+ case uint8:
+ return float64(x) <= float64(y)
+ case uint16:
+ return float64(x) <= float64(y)
+ case uint32:
+ return float64(x) <= float64(y)
+ case uint64:
+ return float64(x) <= float64(y)
+ case int:
+ return float64(x) <= float64(y)
+ case int8:
+ return float64(x) <= float64(y)
+ case int16:
+ return float64(x) <= float64(y)
+ case int32:
+ return float64(x) <= float64(y)
+ case int64:
+ return float64(x) <= float64(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) <= float64(y)
+ case uint8:
+ return float64(x) <= float64(y)
+ case uint16:
+ return float64(x) <= float64(y)
+ case uint32:
+ return float64(x) <= float64(y)
+ case uint64:
+ return float64(x) <= float64(y)
+ case int:
+ return float64(x) <= float64(y)
+ case int8:
+ return float64(x) <= float64(y)
+ case int16:
+ return float64(x) <= float64(y)
+ case int32:
+ return float64(x) <= float64(y)
+ case int64:
+ return float64(x) <= float64(y)
+ case float32:
+ return float64(x) <= float64(y)
+ case float64:
+ return float64(x) <= float64(y)
+ }
+ case string:
+ switch y := b.(type) {
+ case string:
+ return x <= y
+ }
+ case time.Time:
+ switch y := b.(type) {
+ case time.Time:
+ return x.Before(y) || x.Equal(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x <= y
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T <= %T", a, b))
+}
+
+func MoreOrEqual(a, b interface{}) bool {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) >= int(y)
+ case uint8:
+ return int(x) >= int(y)
+ case uint16:
+ return int(x) >= int(y)
+ case uint32:
+ return int(x) >= int(y)
+ case uint64:
+ return int(x) >= int(y)
+ case int:
+ return int(x) >= int(y)
+ case int8:
+ return int(x) >= int(y)
+ case int16:
+ return int(x) >= int(y)
+ case int32:
+ return int(x) >= int(y)
+ case int64:
+ return int(x) >= int(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) >= float64(y)
+ case uint8:
+ return float64(x) >= float64(y)
+ case uint16:
+ return float64(x) >= float64(y)
+ case uint32:
+ return float64(x) >= float64(y)
+ case uint64:
+ return float64(x) >= float64(y)
+ case int:
+ return float64(x) >= float64(y)
+ case int8:
+ return float64(x) >= float64(y)
+ case int16:
+ return float64(x) >= float64(y)
+ case int32:
+ return float64(x) >= float64(y)
+ case int64:
+ return float64(x) >= float64(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) >= float64(y)
+ case uint8:
+ return float64(x) >= float64(y)
+ case uint16:
+ return float64(x) >= float64(y)
+ case uint32:
+ return float64(x) >= float64(y)
+ case uint64:
+ return float64(x) >= float64(y)
+ case int:
+ return float64(x) >= float64(y)
+ case int8:
+ return float64(x) >= float64(y)
+ case int16:
+ return float64(x) >= float64(y)
+ case int32:
+ return float64(x) >= float64(y)
+ case int64:
+ return float64(x) >= float64(y)
+ case float32:
+ return float64(x) >= float64(y)
+ case float64:
+ return float64(x) >= float64(y)
+ }
+ case string:
+ switch y := b.(type) {
+ case string:
+ return x >= y
+ }
+ case time.Time:
+ switch y := b.(type) {
+ case time.Time:
+ return x.After(y) || x.Equal(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x >= y
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T >= %T", a, b))
+}
+
+func Add(a, b interface{}) interface{} {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) + int(y)
+ case uint8:
+ return int(x) + int(y)
+ case uint16:
+ return int(x) + int(y)
+ case uint32:
+ return int(x) + int(y)
+ case uint64:
+ return int(x) + int(y)
+ case int:
+ return int(x) + int(y)
+ case int8:
+ return int(x) + int(y)
+ case int16:
+ return int(x) + int(y)
+ case int32:
+ return int(x) + int(y)
+ case int64:
+ return int(x) + int(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) + float64(y)
+ case uint8:
+ return float64(x) + float64(y)
+ case uint16:
+ return float64(x) + float64(y)
+ case uint32:
+ return float64(x) + float64(y)
+ case uint64:
+ return float64(x) + float64(y)
+ case int:
+ return float64(x) + float64(y)
+ case int8:
+ return float64(x) + float64(y)
+ case int16:
+ return float64(x) + float64(y)
+ case int32:
+ return float64(x) + float64(y)
+ case int64:
+ return float64(x) + float64(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) + float64(y)
+ case uint8:
+ return float64(x) + float64(y)
+ case uint16:
+ return float64(x) + float64(y)
+ case uint32:
+ return float64(x) + float64(y)
+ case uint64:
+ return float64(x) + float64(y)
+ case int:
+ return float64(x) + float64(y)
+ case int8:
+ return float64(x) + float64(y)
+ case int16:
+ return float64(x) + float64(y)
+ case int32:
+ return float64(x) + float64(y)
+ case int64:
+ return float64(x) + float64(y)
+ case float32:
+ return float64(x) + float64(y)
+ case float64:
+ return float64(x) + float64(y)
+ }
+ case string:
+ switch y := b.(type) {
+ case string:
+ return x + y
+ }
+ case time.Time:
+ switch y := b.(type) {
+ case time.Duration:
+ return x.Add(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Time:
+ return y.Add(x)
+ case time.Duration:
+ return x + y
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T + %T", a, b))
+}
+
+func Subtract(a, b interface{}) interface{} {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) - int(y)
+ case uint8:
+ return int(x) - int(y)
+ case uint16:
+ return int(x) - int(y)
+ case uint32:
+ return int(x) - int(y)
+ case uint64:
+ return int(x) - int(y)
+ case int:
+ return int(x) - int(y)
+ case int8:
+ return int(x) - int(y)
+ case int16:
+ return int(x) - int(y)
+ case int32:
+ return int(x) - int(y)
+ case int64:
+ return int(x) - int(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) - float64(y)
+ case uint8:
+ return float64(x) - float64(y)
+ case uint16:
+ return float64(x) - float64(y)
+ case uint32:
+ return float64(x) - float64(y)
+ case uint64:
+ return float64(x) - float64(y)
+ case int:
+ return float64(x) - float64(y)
+ case int8:
+ return float64(x) - float64(y)
+ case int16:
+ return float64(x) - float64(y)
+ case int32:
+ return float64(x) - float64(y)
+ case int64:
+ return float64(x) - float64(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) - float64(y)
+ case uint8:
+ return float64(x) - float64(y)
+ case uint16:
+ return float64(x) - float64(y)
+ case uint32:
+ return float64(x) - float64(y)
+ case uint64:
+ return float64(x) - float64(y)
+ case int:
+ return float64(x) - float64(y)
+ case int8:
+ return float64(x) - float64(y)
+ case int16:
+ return float64(x) - float64(y)
+ case int32:
+ return float64(x) - float64(y)
+ case int64:
+ return float64(x) - float64(y)
+ case float32:
+ return float64(x) - float64(y)
+ case float64:
+ return float64(x) - float64(y)
+ }
+ case time.Time:
+ switch y := b.(type) {
+ case time.Time:
+ return x.Sub(y)
+ case time.Duration:
+ return x.Add(-y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x - y
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T - %T", a, b))
+}
+
+func Multiply(a, b interface{}) interface{} {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) * int(y)
+ case uint8:
+ return int(x) * int(y)
+ case uint16:
+ return int(x) * int(y)
+ case uint32:
+ return int(x) * int(y)
+ case uint64:
+ return int(x) * int(y)
+ case int:
+ return int(x) * int(y)
+ case int8:
+ return int(x) * int(y)
+ case int16:
+ return int(x) * int(y)
+ case int32:
+ return int(x) * int(y)
+ case int64:
+ return int(x) * int(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) * float64(y)
+ case uint8:
+ return float64(x) * float64(y)
+ case uint16:
+ return float64(x) * float64(y)
+ case uint32:
+ return float64(x) * float64(y)
+ case uint64:
+ return float64(x) * float64(y)
+ case int:
+ return float64(x) * float64(y)
+ case int8:
+ return float64(x) * float64(y)
+ case int16:
+ return float64(x) * float64(y)
+ case int32:
+ return float64(x) * float64(y)
+ case int64:
+ return float64(x) * float64(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return float64(x) * float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) * float64(y)
+ case uint8:
+ return float64(x) * float64(y)
+ case uint16:
+ return float64(x) * float64(y)
+ case uint32:
+ return float64(x) * float64(y)
+ case uint64:
+ return float64(x) * float64(y)
+ case int:
+ return float64(x) * float64(y)
+ case int8:
+ return float64(x) * float64(y)
+ case int16:
+ return float64(x) * float64(y)
+ case int32:
+ return float64(x) * float64(y)
+ case int64:
+ return float64(x) * float64(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return float64(x) * float64(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case uint:
+ return time.Duration(x) * time.Duration(y)
+ case uint8:
+ return time.Duration(x) * time.Duration(y)
+ case uint16:
+ return time.Duration(x) * time.Duration(y)
+ case uint32:
+ return time.Duration(x) * time.Duration(y)
+ case uint64:
+ return time.Duration(x) * time.Duration(y)
+ case int:
+ return time.Duration(x) * time.Duration(y)
+ case int8:
+ return time.Duration(x) * time.Duration(y)
+ case int16:
+ return time.Duration(x) * time.Duration(y)
+ case int32:
+ return time.Duration(x) * time.Duration(y)
+ case int64:
+ return time.Duration(x) * time.Duration(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T * %T", a, b))
+}
+
+func Divide(a, b interface{}) float64 {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case float32:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ case float64:
+ switch y := b.(type) {
+ case uint:
+ return float64(x) / float64(y)
+ case uint8:
+ return float64(x) / float64(y)
+ case uint16:
+ return float64(x) / float64(y)
+ case uint32:
+ return float64(x) / float64(y)
+ case uint64:
+ return float64(x) / float64(y)
+ case int:
+ return float64(x) / float64(y)
+ case int8:
+ return float64(x) / float64(y)
+ case int16:
+ return float64(x) / float64(y)
+ case int32:
+ return float64(x) / float64(y)
+ case int64:
+ return float64(x) / float64(y)
+ case float32:
+ return float64(x) / float64(y)
+ case float64:
+ return float64(x) / float64(y)
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T / %T", a, b))
+}
+
+func Modulo(a, b interface{}) int {
+ switch x := a.(type) {
+ case uint:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case uint8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case uint16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case uint32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case uint64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case int:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case int8:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case int16:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case int32:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ case int64:
+ switch y := b.(type) {
+ case uint:
+ return int(x) % int(y)
+ case uint8:
+ return int(x) % int(y)
+ case uint16:
+ return int(x) % int(y)
+ case uint32:
+ return int(x) % int(y)
+ case uint64:
+ return int(x) % int(y)
+ case int:
+ return int(x) % int(y)
+ case int8:
+ return int(x) % int(y)
+ case int16:
+ return int(x) % int(y)
+ case int32:
+ return int(x) % int(y)
+ case int64:
+ return int(x) % int(y)
+ }
+ }
+ panic(fmt.Sprintf("invalid operation: %T %% %T", a, b))
+}
diff --git a/vendor/github.com/expr-lang/expr/vm/runtime/runtime.go b/vendor/github.com/expr-lang/expr/vm/runtime/runtime.go
new file mode 100644
index 00000000..cd48a280
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/runtime/runtime.go
@@ -0,0 +1,394 @@
+package runtime
+
+//go:generate sh -c "go run ./helpers > ./helpers[generated].go"
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+
+ "github.com/expr-lang/expr/internal/deref"
+)
+
+func Fetch(from, i any) any {
+ v := reflect.ValueOf(from)
+ if v.Kind() == reflect.Invalid {
+ panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
+ }
+
+ // Methods can be defined on any type.
+ if v.NumMethod() > 0 {
+ if methodName, ok := i.(string); ok {
+ method := v.MethodByName(methodName)
+ if method.IsValid() {
+ return method.Interface()
+ }
+ }
+ }
+
+ // Structs, maps, and slices can be access through a pointer or through
+ // a value, when they are accessed through a pointer we don't want to
+ // copy them to a value.
+ // De-reference everything if necessary (interface and pointers)
+ v = deref.Value(v)
+
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.String:
+ index := ToInt(i)
+ l := v.Len()
+ if index < 0 {
+ index = l + index
+ }
+ if index < 0 || index >= l {
+ panic(fmt.Sprintf("index out of range: %v (array length is %v)", index, l))
+ }
+ value := v.Index(index)
+ if value.IsValid() {
+ return value.Interface()
+ }
+
+ case reflect.Map:
+ var value reflect.Value
+ if i == nil {
+ value = v.MapIndex(reflect.Zero(v.Type().Key()))
+ } else {
+ value = v.MapIndex(reflect.ValueOf(i))
+ }
+ if value.IsValid() {
+ return value.Interface()
+ } else {
+ elem := reflect.TypeOf(from).Elem()
+ return reflect.Zero(elem).Interface()
+ }
+
+ case reflect.Struct:
+ fieldName := i.(string)
+ value := v.FieldByNameFunc(func(name string) bool {
+ field, _ := v.Type().FieldByName(name)
+ if field.Tag.Get("expr") == fieldName {
+ return true
+ }
+ return name == fieldName
+ })
+ if value.IsValid() {
+ return value.Interface()
+ }
+ }
+ panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
+}
+
+type Field struct {
+ Index []int
+ Path []string
+}
+
+func FetchField(from any, field *Field) any {
+ v := reflect.ValueOf(from)
+ if v.Kind() != reflect.Invalid {
+ v = reflect.Indirect(v)
+
+ // We can use v.FieldByIndex here, but it will panic if the field
+ // is not exists. And we need to recover() to generate a more
+ // user-friendly error message.
+ // Also, our fieldByIndex() function is slightly faster than the
+ // v.FieldByIndex() function as we don't need to verify what a field
+ // is a struct as we already did it on compilation step.
+ value := fieldByIndex(v, field)
+ if value.IsValid() {
+ return value.Interface()
+ }
+ }
+ panic(fmt.Sprintf("cannot get %v from %T", field.Path[0], from))
+}
+
+func fieldByIndex(v reflect.Value, field *Field) reflect.Value {
+ if len(field.Index) == 1 {
+ return v.Field(field.Index[0])
+ }
+ for i, x := range field.Index {
+ if i > 0 {
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ panic(fmt.Sprintf("cannot get %v from %v", field.Path[i], field.Path[i-1]))
+ }
+ v = v.Elem()
+ }
+ }
+ v = v.Field(x)
+ }
+ return v
+}
+
+type Method struct {
+ Index int
+ Name string
+}
+
+func FetchMethod(from any, method *Method) any {
+ v := reflect.ValueOf(from)
+ kind := v.Kind()
+ if kind != reflect.Invalid {
+ // Methods can be defined on any type, no need to dereference.
+ method := v.Method(method.Index)
+ if method.IsValid() {
+ return method.Interface()
+ }
+ }
+ panic(fmt.Sprintf("cannot fetch %v from %T", method.Name, from))
+}
+
+func Slice(array, from, to any) any {
+ v := reflect.ValueOf(array)
+
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.String:
+ length := v.Len()
+ a, b := ToInt(from), ToInt(to)
+ if a < 0 {
+ a = length + a
+ }
+ if a < 0 {
+ a = 0
+ }
+ if b < 0 {
+ b = length + b
+ }
+ if b < 0 {
+ b = 0
+ }
+ if b > length {
+ b = length
+ }
+ if a > b {
+ a = b
+ }
+ value := v.Slice(a, b)
+ if value.IsValid() {
+ return value.Interface()
+ }
+
+ case reflect.Ptr:
+ value := v.Elem()
+ if value.IsValid() {
+ return Slice(value.Interface(), from, to)
+ }
+
+ }
+ panic(fmt.Sprintf("cannot slice %v", from))
+}
+
+func In(needle any, array any) bool {
+ if array == nil {
+ return false
+ }
+ v := reflect.ValueOf(array)
+
+ switch v.Kind() {
+
+ case reflect.Array, reflect.Slice:
+ for i := 0; i < v.Len(); i++ {
+ value := v.Index(i)
+ if value.IsValid() {
+ if Equal(value.Interface(), needle) {
+ return true
+ }
+ }
+ }
+ return false
+
+ case reflect.Map:
+ var value reflect.Value
+ if needle == nil {
+ value = v.MapIndex(reflect.Zero(v.Type().Key()))
+ } else {
+ value = v.MapIndex(reflect.ValueOf(needle))
+ }
+ if value.IsValid() {
+ return true
+ }
+ return false
+
+ case reflect.Struct:
+ n := reflect.ValueOf(needle)
+ if !n.IsValid() || n.Kind() != reflect.String {
+ panic(fmt.Sprintf("cannot use %T as field name of %T", needle, array))
+ }
+ value := v.FieldByName(n.String())
+ if value.IsValid() {
+ return true
+ }
+ return false
+
+ case reflect.Ptr:
+ value := v.Elem()
+ if value.IsValid() {
+ return In(needle, value.Interface())
+ }
+ return false
+ }
+
+ panic(fmt.Sprintf(`operator "in" not defined on %T`, array))
+}
+
+func Len(a any) int {
+ v := reflect.ValueOf(a)
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+ return v.Len()
+ default:
+ panic(fmt.Sprintf("invalid argument for len (type %T)", a))
+ }
+}
+
+func Negate(i any) any {
+ switch v := i.(type) {
+ case float32:
+ return -v
+ case float64:
+ return -v
+ case int:
+ return -v
+ case int8:
+ return -v
+ case int16:
+ return -v
+ case int32:
+ return -v
+ case int64:
+ return -v
+ case uint:
+ return -v
+ case uint8:
+ return -v
+ case uint16:
+ return -v
+ case uint32:
+ return -v
+ case uint64:
+ return -v
+ default:
+ panic(fmt.Sprintf("invalid operation: - %T", v))
+ }
+}
+
+func Exponent(a, b any) float64 {
+ return math.Pow(ToFloat64(a), ToFloat64(b))
+}
+
+func MakeRange(min, max int) []int {
+ size := max - min + 1
+ if size <= 0 {
+ return []int{}
+ }
+ rng := make([]int, size)
+ for i := range rng {
+ rng[i] = min + i
+ }
+ return rng
+}
+
+func ToInt(a any) int {
+ switch x := a.(type) {
+ case float32:
+ return int(x)
+ case float64:
+ return int(x)
+ case int:
+ return x
+ case int8:
+ return int(x)
+ case int16:
+ return int(x)
+ case int32:
+ return int(x)
+ case int64:
+ return int(x)
+ case uint:
+ return int(x)
+ case uint8:
+ return int(x)
+ case uint16:
+ return int(x)
+ case uint32:
+ return int(x)
+ case uint64:
+ return int(x)
+ default:
+ panic(fmt.Sprintf("invalid operation: int(%T)", x))
+ }
+}
+
+func ToInt64(a any) int64 {
+ switch x := a.(type) {
+ case float32:
+ return int64(x)
+ case float64:
+ return int64(x)
+ case int:
+ return int64(x)
+ case int8:
+ return int64(x)
+ case int16:
+ return int64(x)
+ case int32:
+ return int64(x)
+ case int64:
+ return x
+ case uint:
+ return int64(x)
+ case uint8:
+ return int64(x)
+ case uint16:
+ return int64(x)
+ case uint32:
+ return int64(x)
+ case uint64:
+ return int64(x)
+ default:
+ panic(fmt.Sprintf("invalid operation: int64(%T)", x))
+ }
+}
+
+func ToFloat64(a any) float64 {
+ switch x := a.(type) {
+ case float32:
+ return float64(x)
+ case float64:
+ return x
+ case int:
+ return float64(x)
+ case int8:
+ return float64(x)
+ case int16:
+ return float64(x)
+ case int32:
+ return float64(x)
+ case int64:
+ return float64(x)
+ case uint:
+ return float64(x)
+ case uint8:
+ return float64(x)
+ case uint16:
+ return float64(x)
+ case uint32:
+ return float64(x)
+ case uint64:
+ return float64(x)
+ default:
+ panic(fmt.Sprintf("invalid operation: float(%T)", x))
+ }
+}
+
+func IsNil(v any) bool {
+ if v == nil {
+ return true
+ }
+ r := reflect.ValueOf(v)
+ switch r.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
+ return r.IsNil()
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/expr-lang/expr/vm/runtime/sort.go b/vendor/github.com/expr-lang/expr/vm/runtime/sort.go
new file mode 100644
index 00000000..fb1f340d
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/runtime/sort.go
@@ -0,0 +1,45 @@
+package runtime
+
+type SortBy struct {
+ Desc bool
+ Array []any
+ Values []any
+}
+
+func (s *SortBy) Len() int {
+ return len(s.Array)
+}
+
+func (s *SortBy) Swap(i, j int) {
+ s.Array[i], s.Array[j] = s.Array[j], s.Array[i]
+ s.Values[i], s.Values[j] = s.Values[j], s.Values[i]
+}
+
+func (s *SortBy) Less(i, j int) bool {
+ a, b := s.Values[i], s.Values[j]
+ if s.Desc {
+ return Less(b, a)
+ }
+ return Less(a, b)
+}
+
+type Sort struct {
+ Desc bool
+ Array []any
+}
+
+func (s *Sort) Len() int {
+ return len(s.Array)
+}
+
+func (s *Sort) Swap(i, j int) {
+ s.Array[i], s.Array[j] = s.Array[j], s.Array[i]
+}
+
+func (s *Sort) Less(i, j int) bool {
+ a, b := s.Array[i], s.Array[j]
+ if s.Desc {
+ return Less(b, a)
+ }
+ return Less(a, b)
+}
diff --git a/vendor/github.com/expr-lang/expr/vm/utils.go b/vendor/github.com/expr-lang/expr/vm/utils.go
new file mode 100644
index 00000000..fc2f5e7b
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/utils.go
@@ -0,0 +1,40 @@
+package vm
+
+import (
+ "reflect"
+ "time"
+)
+
+type (
+ Function = func(params ...any) (any, error)
+ SafeFunction = func(params ...any) (any, uint, error)
+)
+
+var (
+ // MemoryBudget represents an upper limit of memory usage.
+ MemoryBudget uint = 1e6
+
+ errorType = reflect.TypeOf((*error)(nil)).Elem()
+)
+
+type Scope struct {
+ Array reflect.Value
+ Index int
+ Len int
+ Count int
+ Acc any
+}
+
+type groupBy = map[any][]any
+
+type Span struct {
+ Name string `json:"name"`
+ Expression string `json:"expression"`
+ Duration int64 `json:"duration"`
+ Children []*Span `json:"children"`
+ start time.Time
+}
+
+func GetSpan(program *Program) *Span {
+ return program.span
+}
diff --git a/vendor/github.com/expr-lang/expr/vm/vm.go b/vendor/github.com/expr-lang/expr/vm/vm.go
new file mode 100644
index 00000000..fa1223b4
--- /dev/null
+++ b/vendor/github.com/expr-lang/expr/vm/vm.go
@@ -0,0 +1,617 @@
+package vm
+
+//go:generate sh -c "go run ./func_types > ./func_types[generated].go"
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/expr-lang/expr/builtin"
+ "github.com/expr-lang/expr/file"
+ "github.com/expr-lang/expr/internal/deref"
+ "github.com/expr-lang/expr/vm/runtime"
+)
+
+func Run(program *Program, env any) (any, error) {
+ if program == nil {
+ return nil, fmt.Errorf("program is nil")
+ }
+
+ vm := VM{}
+ return vm.Run(program, env)
+}
+
+func Debug() *VM {
+ vm := &VM{
+ debug: true,
+ step: make(chan struct{}, 0),
+ curr: make(chan int, 0),
+ }
+ return vm
+}
+
+type VM struct {
+ Stack []any
+ Scopes []*Scope
+ Variables []any
+ ip int
+ memory uint
+ memoryBudget uint
+ debug bool
+ step chan struct{}
+ curr chan int
+}
+
+func (vm *VM) Run(program *Program, env any) (_ any, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ var location file.Location
+ if vm.ip-1 < len(program.locations) {
+ location = program.locations[vm.ip-1]
+ }
+ f := &file.Error{
+ Location: location,
+ Message: fmt.Sprintf("%v", r),
+ }
+ if err, ok := r.(error); ok {
+ f.Wrap(err)
+ }
+ err = f.Bind(program.source)
+ }
+ }()
+
+ if vm.Stack == nil {
+ vm.Stack = make([]any, 0, 2)
+ } else {
+ vm.Stack = vm.Stack[0:0]
+ }
+ if vm.Scopes != nil {
+ vm.Scopes = vm.Scopes[0:0]
+ }
+ if len(vm.Variables) < program.variables {
+ vm.Variables = make([]any, program.variables)
+ }
+
+ vm.memoryBudget = MemoryBudget
+ vm.memory = 0
+ vm.ip = 0
+
+ for vm.ip < len(program.Bytecode) {
+ if debug && vm.debug {
+ <-vm.step
+ }
+
+ op := program.Bytecode[vm.ip]
+ arg := program.Arguments[vm.ip]
+ vm.ip += 1
+
+ switch op {
+
+ case OpInvalid:
+ panic("invalid opcode")
+
+ case OpPush:
+ vm.push(program.Constants[arg])
+
+ case OpInt:
+ vm.push(arg)
+
+ case OpPop:
+ vm.pop()
+
+ case OpStore:
+ vm.Variables[arg] = vm.pop()
+
+ case OpLoadVar:
+ vm.push(vm.Variables[arg])
+
+ case OpLoadConst:
+ vm.push(runtime.Fetch(env, program.Constants[arg]))
+
+ case OpLoadField:
+ vm.push(runtime.FetchField(env, program.Constants[arg].(*runtime.Field)))
+
+ case OpLoadFast:
+ vm.push(env.(map[string]any)[program.Constants[arg].(string)])
+
+ case OpLoadMethod:
+ vm.push(runtime.FetchMethod(env, program.Constants[arg].(*runtime.Method)))
+
+ case OpLoadFunc:
+ vm.push(program.functions[arg])
+
+ case OpFetch:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Fetch(a, b))
+
+ case OpFetchField:
+ a := vm.pop()
+ vm.push(runtime.FetchField(a, program.Constants[arg].(*runtime.Field)))
+
+ case OpLoadEnv:
+ vm.push(env)
+
+ case OpMethod:
+ a := vm.pop()
+ vm.push(runtime.FetchMethod(a, program.Constants[arg].(*runtime.Method)))
+
+ case OpTrue:
+ vm.push(true)
+
+ case OpFalse:
+ vm.push(false)
+
+ case OpNil:
+ vm.push(nil)
+
+ case OpNegate:
+ v := runtime.Negate(vm.pop())
+ vm.push(v)
+
+ case OpNot:
+ v := vm.pop().(bool)
+ vm.push(!v)
+
+ case OpEqual:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Equal(a, b))
+
+ case OpEqualInt:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(a.(int) == b.(int))
+
+ case OpEqualString:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(a.(string) == b.(string))
+
+ case OpJump:
+ vm.ip += arg
+
+ case OpJumpIfTrue:
+ if vm.current().(bool) {
+ vm.ip += arg
+ }
+
+ case OpJumpIfFalse:
+ if !vm.current().(bool) {
+ vm.ip += arg
+ }
+
+ case OpJumpIfNil:
+ if runtime.IsNil(vm.current()) {
+ vm.ip += arg
+ }
+
+ case OpJumpIfNotNil:
+ if !runtime.IsNil(vm.current()) {
+ vm.ip += arg
+ }
+
+ case OpJumpIfEnd:
+ scope := vm.scope()
+ if scope.Index >= scope.Len {
+ vm.ip += arg
+ }
+
+ case OpJumpBackward:
+ vm.ip -= arg
+
+ case OpIn:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.In(a, b))
+
+ case OpLess:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Less(a, b))
+
+ case OpMore:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.More(a, b))
+
+ case OpLessOrEqual:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.LessOrEqual(a, b))
+
+ case OpMoreOrEqual:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.MoreOrEqual(a, b))
+
+ case OpAdd:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Add(a, b))
+
+ case OpSubtract:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Subtract(a, b))
+
+ case OpMultiply:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Multiply(a, b))
+
+ case OpDivide:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Divide(a, b))
+
+ case OpModulo:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Modulo(a, b))
+
+ case OpExponent:
+ b := vm.pop()
+ a := vm.pop()
+ vm.push(runtime.Exponent(a, b))
+
+ case OpRange:
+ b := vm.pop()
+ a := vm.pop()
+ min := runtime.ToInt(a)
+ max := runtime.ToInt(b)
+ size := max - min + 1
+ if size <= 0 {
+ size = 0
+ }
+ vm.memGrow(uint(size))
+ vm.push(runtime.MakeRange(min, max))
+
+ case OpMatches:
+ b := vm.pop()
+ a := vm.pop()
+ if runtime.IsNil(a) || runtime.IsNil(b) {
+ vm.push(false)
+ break
+ }
+ match, err := regexp.MatchString(b.(string), a.(string))
+ if err != nil {
+ panic(err)
+ }
+ vm.push(match)
+
+ case OpMatchesConst:
+ a := vm.pop()
+ if runtime.IsNil(a) {
+ vm.push(false)
+ break
+ }
+ r := program.Constants[arg].(*regexp.Regexp)
+ vm.push(r.MatchString(a.(string)))
+
+ case OpContains:
+ b := vm.pop()
+ a := vm.pop()
+ if runtime.IsNil(a) || runtime.IsNil(b) {
+ vm.push(false)
+ break
+ }
+ vm.push(strings.Contains(a.(string), b.(string)))
+
+ case OpStartsWith:
+ b := vm.pop()
+ a := vm.pop()
+ if runtime.IsNil(a) || runtime.IsNil(b) {
+ vm.push(false)
+ break
+ }
+ vm.push(strings.HasPrefix(a.(string), b.(string)))
+
+ case OpEndsWith:
+ b := vm.pop()
+ a := vm.pop()
+ if runtime.IsNil(a) || runtime.IsNil(b) {
+ vm.push(false)
+ break
+ }
+ vm.push(strings.HasSuffix(a.(string), b.(string)))
+
+ case OpSlice:
+ from := vm.pop()
+ to := vm.pop()
+ node := vm.pop()
+ vm.push(runtime.Slice(node, from, to))
+
+ case OpCall:
+ fn := reflect.ValueOf(vm.pop())
+ size := arg
+ in := make([]reflect.Value, size)
+ for i := int(size) - 1; i >= 0; i-- {
+ param := vm.pop()
+ if param == nil && reflect.TypeOf(param) == nil {
+ // In case of nil value and nil type use this hack,
+ // otherwise reflect.Call will panic on zero value.
+ in[i] = reflect.ValueOf(¶m).Elem()
+ } else {
+ in[i] = reflect.ValueOf(param)
+ }
+ }
+ out := fn.Call(in)
+ if len(out) == 2 && out[1].Type() == errorType && !out[1].IsNil() {
+ panic(out[1].Interface().(error))
+ }
+ vm.push(out[0].Interface())
+
+ case OpCall0:
+ out, err := program.functions[arg]()
+ if err != nil {
+ panic(err)
+ }
+ vm.push(out)
+
+ case OpCall1:
+ a := vm.pop()
+ out, err := program.functions[arg](a)
+ if err != nil {
+ panic(err)
+ }
+ vm.push(out)
+
+ case OpCall2:
+ b := vm.pop()
+ a := vm.pop()
+ out, err := program.functions[arg](a, b)
+ if err != nil {
+ panic(err)
+ }
+ vm.push(out)
+
+ case OpCall3:
+ c := vm.pop()
+ b := vm.pop()
+ a := vm.pop()
+ out, err := program.functions[arg](a, b, c)
+ if err != nil {
+ panic(err)
+ }
+ vm.push(out)
+
+ case OpCallN:
+ fn := vm.pop().(Function)
+ size := arg
+ in := make([]any, size)
+ for i := int(size) - 1; i >= 0; i-- {
+ in[i] = vm.pop()
+ }
+ out, err := fn(in...)
+ if err != nil {
+ panic(err)
+ }
+ vm.push(out)
+
+ case OpCallFast:
+ fn := vm.pop().(func(...any) any)
+ size := arg
+ in := make([]any, size)
+ for i := int(size) - 1; i >= 0; i-- {
+ in[i] = vm.pop()
+ }
+ vm.push(fn(in...))
+
+ case OpCallSafe:
+ fn := vm.pop().(SafeFunction)
+ size := arg
+ in := make([]any, size)
+ for i := int(size) - 1; i >= 0; i-- {
+ in[i] = vm.pop()
+ }
+ out, mem, err := fn(in...)
+ if err != nil {
+ panic(err)
+ }
+ vm.memGrow(mem)
+ vm.push(out)
+
+ case OpCallTyped:
+ vm.push(vm.call(vm.pop(), arg))
+
+ case OpCallBuiltin1:
+ vm.push(builtin.Builtins[arg].Fast(vm.pop()))
+
+ case OpArray:
+ size := vm.pop().(int)
+ vm.memGrow(uint(size))
+ array := make([]any, size)
+ for i := size - 1; i >= 0; i-- {
+ array[i] = vm.pop()
+ }
+ vm.push(array)
+
+ case OpMap:
+ size := vm.pop().(int)
+ vm.memGrow(uint(size))
+ m := make(map[string]any)
+ for i := size - 1; i >= 0; i-- {
+ value := vm.pop()
+ key := vm.pop()
+ m[key.(string)] = value
+ }
+ vm.push(m)
+
+ case OpLen:
+ vm.push(runtime.Len(vm.current()))
+
+ case OpCast:
+ switch arg {
+ case 0:
+ vm.push(runtime.ToInt(vm.pop()))
+ case 1:
+ vm.push(runtime.ToInt64(vm.pop()))
+ case 2:
+ vm.push(runtime.ToFloat64(vm.pop()))
+ }
+
+ case OpDeref:
+ a := vm.pop()
+ vm.push(deref.Deref(a))
+
+ case OpIncrementIndex:
+ vm.scope().Index++
+
+ case OpDecrementIndex:
+ scope := vm.scope()
+ scope.Index--
+
+ case OpIncrementCount:
+ scope := vm.scope()
+ scope.Count++
+
+ case OpGetIndex:
+ vm.push(vm.scope().Index)
+
+ case OpGetCount:
+ scope := vm.scope()
+ vm.push(scope.Count)
+
+ case OpGetLen:
+ scope := vm.scope()
+ vm.push(scope.Len)
+
+ case OpGetAcc:
+ vm.push(vm.scope().Acc)
+
+ case OpSetAcc:
+ vm.scope().Acc = vm.pop()
+
+ case OpSetIndex:
+ scope := vm.scope()
+ scope.Index = vm.pop().(int)
+
+ case OpPointer:
+ scope := vm.scope()
+ vm.push(scope.Array.Index(scope.Index).Interface())
+
+ case OpThrow:
+ panic(vm.pop().(error))
+
+ case OpCreate:
+ switch arg {
+ case 1:
+ vm.push(make(groupBy))
+ case 2:
+ scope := vm.scope()
+ var desc bool
+ switch vm.pop().(string) {
+ case "asc":
+ desc = false
+ case "desc":
+ desc = true
+ default:
+ panic("unknown order, use asc or desc")
+ }
+ vm.push(&runtime.SortBy{
+ Desc: desc,
+ Array: make([]any, 0, scope.Len),
+ Values: make([]any, 0, scope.Len),
+ })
+ default:
+ panic(fmt.Sprintf("unknown OpCreate argument %v", arg))
+ }
+
+ case OpGroupBy:
+ scope := vm.scope()
+ key := vm.pop()
+ item := scope.Array.Index(scope.Index).Interface()
+ scope.Acc.(groupBy)[key] = append(scope.Acc.(groupBy)[key], item)
+
+ case OpSortBy:
+ scope := vm.scope()
+ value := vm.pop()
+ item := scope.Array.Index(scope.Index).Interface()
+ sortable := scope.Acc.(*runtime.SortBy)
+ sortable.Array = append(sortable.Array, item)
+ sortable.Values = append(sortable.Values, value)
+
+ case OpSort:
+ scope := vm.scope()
+ sortable := scope.Acc.(*runtime.SortBy)
+ sort.Sort(sortable)
+ vm.memGrow(uint(scope.Len))
+ vm.push(sortable.Array)
+
+ case OpProfileStart:
+ span := program.Constants[arg].(*Span)
+ span.start = time.Now()
+
+ case OpProfileEnd:
+ span := program.Constants[arg].(*Span)
+ span.Duration += time.Since(span.start).Nanoseconds()
+
+ case OpBegin:
+ a := vm.pop()
+ array := reflect.ValueOf(a)
+ vm.Scopes = append(vm.Scopes, &Scope{
+ Array: array,
+ Len: array.Len(),
+ })
+
+ case OpEnd:
+ vm.Scopes = vm.Scopes[:len(vm.Scopes)-1]
+
+ default:
+ panic(fmt.Sprintf("unknown bytecode %#x", op))
+ }
+
+ if debug && vm.debug {
+ vm.curr <- vm.ip
+ }
+ }
+
+ if debug && vm.debug {
+ close(vm.curr)
+ close(vm.step)
+ }
+
+ if len(vm.Stack) > 0 {
+ return vm.pop(), nil
+ }
+
+ return nil, nil
+}
+
+func (vm *VM) push(value any) {
+ vm.Stack = append(vm.Stack, value)
+}
+
+func (vm *VM) current() any {
+ return vm.Stack[len(vm.Stack)-1]
+}
+
+func (vm *VM) pop() any {
+ value := vm.Stack[len(vm.Stack)-1]
+ vm.Stack = vm.Stack[:len(vm.Stack)-1]
+ return value
+}
+
+func (vm *VM) memGrow(size uint) {
+ vm.memory += size
+ if vm.memory >= vm.memoryBudget {
+ panic("memory budget exceeded")
+ }
+}
+
+func (vm *VM) scope() *Scope {
+ return vm.Scopes[len(vm.Scopes)-1]
+}
+
+func (vm *VM) Step() {
+ vm.step <- struct{}{}
+}
+
+func (vm *VM) Position() chan int {
+ return vm.curr
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 44104a97..bc158e04 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -61,6 +61,24 @@ github.com/creachadair/jrpc2/code
github.com/creachadair/jrpc2/handler
github.com/creachadair/jrpc2/metrics
github.com/creachadair/jrpc2/server
+# github.com/expr-lang/expr v1.16.9
+## explicit; go 1.18
+github.com/expr-lang/expr
+github.com/expr-lang/expr/ast
+github.com/expr-lang/expr/builtin
+github.com/expr-lang/expr/checker
+github.com/expr-lang/expr/compiler
+github.com/expr-lang/expr/conf
+github.com/expr-lang/expr/file
+github.com/expr-lang/expr/internal/deref
+github.com/expr-lang/expr/optimizer
+github.com/expr-lang/expr/parser
+github.com/expr-lang/expr/parser/lexer
+github.com/expr-lang/expr/parser/operator
+github.com/expr-lang/expr/parser/utils
+github.com/expr-lang/expr/patcher
+github.com/expr-lang/expr/vm
+github.com/expr-lang/expr/vm/runtime
# github.com/fatih/color v1.7.0
## explicit
github.com/fatih/color