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 @@ +

Zx logo 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