mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2025-10-12 18:51:02 +08:00
chore: upgrade coredns version (#550)
This commit is contained in:
1
vendor/github.com/expr-lang/expr/.gitattributes
generated
vendored
Normal file
1
vendor/github.com/expr-lang/expr/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*\[generated\].go linguist-language=txt
|
11
vendor/github.com/expr-lang/expr/.gitignore
generated
vendored
Normal file
11
vendor/github.com/expr-lang/expr/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.test
|
||||
*.out
|
||||
*.html
|
||||
custom_tests.json
|
||||
pro/
|
||||
test/avs/
|
21
vendor/github.com/expr-lang/expr/LICENSE
generated
vendored
Normal file
21
vendor/github.com/expr-lang/expr/LICENSE
generated
vendored
Normal file
@@ -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.
|
181
vendor/github.com/expr-lang/expr/README.md
generated
vendored
Normal file
181
vendor/github.com/expr-lang/expr/README.md
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
<h1><a href="https://expr-lang.org"><img src="https://expr-lang.org/img/logo.png" alt="Zx logo" height="48"align="right"></a> Expr</h1>
|
||||
|
||||
> [!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).
|
||||
|
||||
[](https://github.com/expr-lang/expr/actions/workflows/test.yml)
|
||||
[](https://goreportcard.com/report/github.com/expr-lang/expr)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:expr)
|
||||
[](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 its 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.
|
||||
* [Tork](https://www.tork.run/) integrates Expr into its workflow execution.
|
||||
* [Critical Moments](https://criticalmoments.io) uses Expr for its mobile realtime conditional targeting system.
|
||||
* [WoodpeckerCI](https://woodpecker-ci.org) uses Expr for [filtering workflows/steps](https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate).
|
||||
* [FastSchema](https://github.com/fastschema/fastschema) - A BaaS leveraging Expr for its customizable and dynamic Access Control system.
|
||||
* [WunderGraph Cosmo](https://github.com/wundergraph/cosmo) - GraphQL Federeration Router uses Expr to customize Middleware behaviour
|
||||
* [SOLO](https://solo.one) uses Expr interally to allow dynamic code execution with custom defined functions.
|
||||
|
||||
[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)
|
||||
|
||||
<p align="center"><img src="https://expr-lang.org/img/gopher-small.png" width="150" /></p>
|
22
vendor/github.com/expr-lang/expr/SECURITY.md
generated
vendored
Normal file
22
vendor/github.com/expr-lang/expr/SECURITY.md
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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.x | :white_check_mark: |
|
||||
| 0.x | :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.
|
59
vendor/github.com/expr-lang/expr/ast/dump.go
generated
vendored
Normal file
59
vendor/github.com/expr-lang/expr/ast/dump.go
generated
vendored
Normal file
@@ -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))
|
||||
}
|
18
vendor/github.com/expr-lang/expr/ast/find.go
generated
vendored
Normal file
18
vendor/github.com/expr-lang/expr/ast/find.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package ast
|
||||
|
||||
func Find(node Node, fn func(node Node) bool) Node {
|
||||
v := &finder{fn: fn}
|
||||
Walk(&node, v)
|
||||
return v.node
|
||||
}
|
||||
|
||||
type finder struct {
|
||||
node Node
|
||||
fn func(node Node) bool
|
||||
}
|
||||
|
||||
func (f *finder) Visit(node *Node) {
|
||||
if f.fn(*node) {
|
||||
f.node = *node
|
||||
}
|
||||
}
|
243
vendor/github.com/expr-lang/expr/ast/node.go
generated
vendored
Normal file
243
vendor/github.com/expr-lang/expr/ast/node.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/expr-lang/expr/checker/nature"
|
||||
"github.com/expr-lang/expr/file"
|
||||
)
|
||||
|
||||
var (
|
||||
anyType = reflect.TypeOf(new(any)).Elem()
|
||||
)
|
||||
|
||||
// Node represents items of abstract syntax tree.
|
||||
type Node interface {
|
||||
Location() file.Location
|
||||
SetLocation(file.Location)
|
||||
Nature() nature.Nature
|
||||
SetNature(nature.Nature)
|
||||
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
|
||||
nature nature.Nature
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Nature returns the nature of the node.
|
||||
func (n *base) Nature() nature.Nature {
|
||||
return n.nature
|
||||
}
|
||||
|
||||
// SetNature sets the nature of the node.
|
||||
func (n *base) SetNature(nature nature.Nature) {
|
||||
n.nature = nature
|
||||
}
|
||||
|
||||
// Type returns the type of the node.
|
||||
func (n *base) Type() reflect.Type {
|
||||
if n.nature.Type == nil {
|
||||
return anyType
|
||||
}
|
||||
return n.nature.Type
|
||||
}
|
||||
|
||||
// SetType sets the type of the node.
|
||||
func (n *base) SetType(t reflect.Type) {
|
||||
n.nature.Type = 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.
|
||||
}
|
||||
|
||||
// PredicateNode represents a predicate.
|
||||
// Example:
|
||||
//
|
||||
// filter(foo, .bar == 1)
|
||||
//
|
||||
// The predicate is ".bar == 1".
|
||||
type PredicateNode 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".
|
||||
}
|
||||
|
||||
// SequenceNode represents a sequence of nodes separated by semicolons.
|
||||
// All nodes are executed, only the last node will be returned.
|
||||
type SequenceNode struct {
|
||||
base
|
||||
Nodes []Node
|
||||
}
|
||||
|
||||
// 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.
|
||||
}
|
253
vendor/github.com/expr-lang/expr/ast/print.go
generated
vendored
Normal file
253
vendor/github.com/expr-lang/expr/ast/print.go
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
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 := n.Operator
|
||||
if n.Operator == "not" {
|
||||
op = fmt.Sprintf("%s ", n.Operator)
|
||||
}
|
||||
wrap := false
|
||||
switch b := n.Node.(type) {
|
||||
case *BinaryNode:
|
||||
if operator.Binary[b.Operator].Precedence <
|
||||
operator.Unary[n.Operator].Precedence {
|
||||
wrap = true
|
||||
}
|
||||
case *ConditionalNode:
|
||||
wrap = true
|
||||
}
|
||||
if wrap {
|
||||
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 l, ok := n.Left.(*UnaryNode); ok {
|
||||
if operator.Unary[l.Operator].Precedence <
|
||||
operator.Binary[n.Operator].Precedence {
|
||||
lwrap = true
|
||||
}
|
||||
}
|
||||
if lb, ok := n.Left.(*BinaryNode); ok {
|
||||
if operator.Less(lb.Operator, n.Operator) {
|
||||
lwrap = true
|
||||
}
|
||||
if operator.Binary[lb.Operator].Precedence ==
|
||||
operator.Binary[n.Operator].Precedence &&
|
||||
operator.Binary[n.Operator].Associativity == operator.Right {
|
||||
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.Binary[rb.Operator].Precedence ==
|
||||
operator.Binary[n.Operator].Precedence &&
|
||||
operator.Binary[n.Operator].Associativity == operator.Left {
|
||||
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 *PredicateNode) 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 *SequenceNode) String() string {
|
||||
nodes := make([]string, len(n.Nodes))
|
||||
for i, node := range n.Nodes {
|
||||
nodes[i] = node.String()
|
||||
}
|
||||
return strings.Join(nodes, "; ")
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
78
vendor/github.com/expr-lang/expr/ast/visitor.go
generated
vendored
Normal file
78
vendor/github.com/expr-lang/expr/ast/visitor.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
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 *PredicateNode:
|
||||
Walk(&n.Node, v)
|
||||
case *PointerNode:
|
||||
case *VariableDeclaratorNode:
|
||||
Walk(&n.Value, v)
|
||||
Walk(&n.Expr, v)
|
||||
case *SequenceNode:
|
||||
for i := range n.Nodes {
|
||||
Walk(&n.Nodes[i], 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)
|
||||
}
|
1069
vendor/github.com/expr-lang/expr/builtin/builtin.go
generated
vendored
Normal file
1069
vendor/github.com/expr-lang/expr/builtin/builtin.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
23
vendor/github.com/expr-lang/expr/builtin/function.go
generated
vendored
Normal file
23
vendor/github.com/expr-lang/expr/builtin/function.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
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)
|
||||
Deref func(i int, arg reflect.Type) bool
|
||||
Predicate bool
|
||||
}
|
||||
|
||||
func (f *Function) Type() reflect.Type {
|
||||
if len(f.Types) > 0 {
|
||||
return f.Types[0]
|
||||
}
|
||||
return reflect.TypeOf(f.Func)
|
||||
}
|
433
vendor/github.com/expr-lang/expr/builtin/lib.go
generated
vendored
Normal file
433
vendor/github.com/expr-lang/expr/builtin/lib.go
generated
vendored
Normal file
@@ -0,0 +1,433 @@
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/expr-lang/expr/internal/deref"
|
||||
"github.com/expr-lang/expr/vm/runtime"
|
||||
)
|
||||
|
||||
func Len(x any) any {
|
||||
v := reflect.ValueOf(x)
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Map:
|
||||
return v.Len()
|
||||
case reflect.String:
|
||||
return utf8.RuneCountInString(v.String())
|
||||
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)
|
||||
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(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(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(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
|
||||
}
|
||||
|
||||
func flatten(arg reflect.Value) []any {
|
||||
ret := []any{}
|
||||
for i := 0; i < arg.Len(); i++ {
|
||||
v := deref.Value(arg.Index(i))
|
||||
if v.Kind() == reflect.Array || v.Kind() == reflect.Slice {
|
||||
x := flatten(v)
|
||||
ret = append(ret, x...)
|
||||
} else {
|
||||
ret = append(ret, v.Interface())
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func get(params ...any) (out any, err error) {
|
||||
from := params[0]
|
||||
i := params[1]
|
||||
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(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
index := runtime.ToInt(i)
|
||||
l := v.Len()
|
||||
if index < 0 {
|
||||
index = l + index
|
||||
}
|
||||
if 0 <= index && index < l {
|
||||
value := v.Index(index)
|
||||
if value.IsValid() {
|
||||
return value.Interface(), nil
|
||||
}
|
||||
}
|
||||
|
||||
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(), nil
|
||||
}
|
||||
|
||||
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(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Main difference from runtime.Fetch
|
||||
// is that we return `nil` instead of panic.
|
||||
return nil, nil
|
||||
}
|
90
vendor/github.com/expr-lang/expr/builtin/utils.go
generated
vendored
Normal file
90
vendor/github.com/expr-lang/expr/builtin/utils.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/expr-lang/expr/internal/deref"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
t = deref.Type(t)
|
||||
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)),
|
||||
}
|
||||
}
|
38
vendor/github.com/expr-lang/expr/builtin/validation.go
generated
vendored
Normal file
38
vendor/github.com/expr-lang/expr/builtin/validation.go
generated
vendored
Normal file
@@ -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])
|
||||
}
|
||||
}
|
1285
vendor/github.com/expr-lang/expr/checker/checker.go
generated
vendored
Normal file
1285
vendor/github.com/expr-lang/expr/checker/checker.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
129
vendor/github.com/expr-lang/expr/checker/info.go
generated
vendored
Normal file
129
vendor/github.com/expr-lang/expr/checker/info.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
package checker
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/expr-lang/expr/ast"
|
||||
. "github.com/expr-lang/expr/checker/nature"
|
||||
"github.com/expr-lang/expr/vm"
|
||||
)
|
||||
|
||||
func FieldIndex(env Nature, node ast.Node) (bool, []int, string) {
|
||||
switch n := node.(type) {
|
||||
case *ast.IdentifierNode:
|
||||
if env.Kind() == reflect.Struct {
|
||||
if field, ok := env.Get(n.Value); ok && len(field.FieldIndex) > 0 {
|
||||
return true, field.FieldIndex, n.Value
|
||||
}
|
||||
}
|
||||
case *ast.MemberNode:
|
||||
base := n.Node.Nature()
|
||||
base = base.Deref()
|
||||
if base.Kind() == reflect.Struct {
|
||||
if prop, ok := n.Property.(*ast.StringNode); ok {
|
||||
name := prop.Value
|
||||
if field, ok := base.FieldByName(name); ok {
|
||||
return true, field.FieldIndex, name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil, ""
|
||||
}
|
||||
|
||||
func MethodIndex(env Nature, node ast.Node) (bool, int, string) {
|
||||
switch n := node.(type) {
|
||||
case *ast.IdentifierNode:
|
||||
if env.Kind() == reflect.Struct {
|
||||
if m, ok := env.Get(n.Value); ok {
|
||||
return m.Method, m.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 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
|
||||
}
|
261
vendor/github.com/expr-lang/expr/checker/nature/nature.go
generated
vendored
Normal file
261
vendor/github.com/expr-lang/expr/checker/nature/nature.go
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
package nature
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/expr-lang/expr/builtin"
|
||||
"github.com/expr-lang/expr/internal/deref"
|
||||
)
|
||||
|
||||
var (
|
||||
unknown = Nature{}
|
||||
)
|
||||
|
||||
type Nature struct {
|
||||
Type reflect.Type // Type of the value. If nil, then value is unknown.
|
||||
Func *builtin.Function // Used to pass function type from callee to CallNode.
|
||||
ArrayOf *Nature // Elem nature of array type (usually Type is []any, but ArrayOf can be any nature).
|
||||
PredicateOut *Nature // Out nature of predicate.
|
||||
Fields map[string]Nature // Fields of map type.
|
||||
DefaultMapValue *Nature // Default value of map type.
|
||||
Strict bool // If map is types.StrictMap.
|
||||
Nil bool // If value is nil.
|
||||
Method bool // If value retrieved from method. Usually used to determine amount of in arguments.
|
||||
MethodIndex int // Index of method in type.
|
||||
FieldIndex []int // Index of field in type.
|
||||
}
|
||||
|
||||
func (n Nature) IsAny() bool {
|
||||
return n.Kind() == reflect.Interface && n.NumMethods() == 0
|
||||
}
|
||||
|
||||
func (n Nature) IsUnknown() bool {
|
||||
switch {
|
||||
case n.Type == nil && !n.Nil:
|
||||
return true
|
||||
case n.IsAny():
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (n Nature) String() string {
|
||||
if n.Type != nil {
|
||||
return n.Type.String()
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func (n Nature) Deref() Nature {
|
||||
if n.Type != nil {
|
||||
n.Type = deref.Type(n.Type)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n Nature) Kind() reflect.Kind {
|
||||
if n.Type != nil {
|
||||
return n.Type.Kind()
|
||||
}
|
||||
return reflect.Invalid
|
||||
}
|
||||
|
||||
func (n Nature) Key() Nature {
|
||||
if n.Kind() == reflect.Map {
|
||||
return Nature{Type: n.Type.Key()}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
func (n Nature) Elem() Nature {
|
||||
switch n.Kind() {
|
||||
case reflect.Ptr:
|
||||
return Nature{Type: n.Type.Elem()}
|
||||
case reflect.Map:
|
||||
if n.DefaultMapValue != nil {
|
||||
return *n.DefaultMapValue
|
||||
}
|
||||
return Nature{Type: n.Type.Elem()}
|
||||
case reflect.Array, reflect.Slice:
|
||||
if n.ArrayOf != nil {
|
||||
return *n.ArrayOf
|
||||
}
|
||||
return Nature{Type: n.Type.Elem()}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
func (n Nature) AssignableTo(nt Nature) bool {
|
||||
if n.Nil {
|
||||
// Untyped nil is assignable to any interface, but implements only the empty interface.
|
||||
if nt.IsAny() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if n.Type == nil || nt.Type == nil {
|
||||
return false
|
||||
}
|
||||
return n.Type.AssignableTo(nt.Type)
|
||||
}
|
||||
|
||||
func (n Nature) NumMethods() int {
|
||||
if n.Type == nil {
|
||||
return 0
|
||||
}
|
||||
return n.Type.NumMethod()
|
||||
}
|
||||
|
||||
func (n Nature) MethodByName(name string) (Nature, bool) {
|
||||
if n.Type == nil {
|
||||
return unknown, false
|
||||
}
|
||||
method, ok := n.Type.MethodByName(name)
|
||||
if !ok {
|
||||
return unknown, false
|
||||
}
|
||||
|
||||
if n.Type.Kind() == 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 Nature{Type: method.Type}, true
|
||||
} else {
|
||||
return Nature{
|
||||
Type: method.Type,
|
||||
Method: true,
|
||||
MethodIndex: method.Index,
|
||||
}, true
|
||||
}
|
||||
}
|
||||
|
||||
func (n Nature) NumIn() int {
|
||||
if n.Type == nil {
|
||||
return 0
|
||||
}
|
||||
return n.Type.NumIn()
|
||||
}
|
||||
|
||||
func (n Nature) In(i int) Nature {
|
||||
if n.Type == nil {
|
||||
return unknown
|
||||
}
|
||||
return Nature{Type: n.Type.In(i)}
|
||||
}
|
||||
|
||||
func (n Nature) NumOut() int {
|
||||
if n.Type == nil {
|
||||
return 0
|
||||
}
|
||||
return n.Type.NumOut()
|
||||
}
|
||||
|
||||
func (n Nature) Out(i int) Nature {
|
||||
if n.Type == nil {
|
||||
return unknown
|
||||
}
|
||||
return Nature{Type: n.Type.Out(i)}
|
||||
}
|
||||
|
||||
func (n Nature) IsVariadic() bool {
|
||||
if n.Type == nil {
|
||||
return false
|
||||
}
|
||||
return n.Type.IsVariadic()
|
||||
}
|
||||
|
||||
func (n Nature) FieldByName(name string) (Nature, bool) {
|
||||
if n.Type == nil {
|
||||
return unknown, false
|
||||
}
|
||||
field, ok := fetchField(n.Type, name)
|
||||
return Nature{Type: field.Type, FieldIndex: field.Index}, ok
|
||||
}
|
||||
|
||||
func (n Nature) PkgPath() string {
|
||||
if n.Type == nil {
|
||||
return ""
|
||||
}
|
||||
return n.Type.PkgPath()
|
||||
}
|
||||
|
||||
func (n Nature) IsFastMap() bool {
|
||||
if n.Type == nil {
|
||||
return false
|
||||
}
|
||||
if n.Type.Kind() == reflect.Map &&
|
||||
n.Type.Key().Kind() == reflect.String &&
|
||||
n.Type.Elem().Kind() == reflect.Interface {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (n Nature) Get(name string) (Nature, bool) {
|
||||
if n.Type == nil {
|
||||
return unknown, false
|
||||
}
|
||||
|
||||
if m, ok := n.MethodByName(name); ok {
|
||||
return m, true
|
||||
}
|
||||
|
||||
t := deref.Type(n.Type)
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Struct:
|
||||
if f, ok := fetchField(t, name); ok {
|
||||
return Nature{
|
||||
Type: f.Type,
|
||||
FieldIndex: f.Index,
|
||||
}, true
|
||||
}
|
||||
case reflect.Map:
|
||||
if f, ok := n.Fields[name]; ok {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
return unknown, false
|
||||
}
|
||||
|
||||
func (n Nature) All() map[string]Nature {
|
||||
table := make(map[string]Nature)
|
||||
|
||||
if n.Type == nil {
|
||||
return table
|
||||
}
|
||||
|
||||
for i := 0; i < n.Type.NumMethod(); i++ {
|
||||
method := n.Type.Method(i)
|
||||
table[method.Name] = Nature{
|
||||
Type: method.Type,
|
||||
Method: true,
|
||||
MethodIndex: method.Index,
|
||||
}
|
||||
}
|
||||
|
||||
t := deref.Type(n.Type)
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Struct:
|
||||
for name, nt := range StructFields(t) {
|
||||
if _, ok := table[name]; ok {
|
||||
continue
|
||||
}
|
||||
table[name] = nt
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
for key, nt := range n.Fields {
|
||||
if _, ok := table[key]; ok {
|
||||
continue
|
||||
}
|
||||
table[key] = nt
|
||||
}
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
76
vendor/github.com/expr-lang/expr/checker/nature/utils.go
generated
vendored
Normal file
76
vendor/github.com/expr-lang/expr/checker/nature/utils.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package nature
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/expr-lang/expr/internal/deref"
|
||||
)
|
||||
|
||||
func fieldName(field reflect.StructField) string {
|
||||
if taggedName := field.Tag.Get("expr"); taggedName != "" {
|
||||
return taggedName
|
||||
}
|
||||
return field.Name
|
||||
}
|
||||
|
||||
func fetchField(t reflect.Type, name string) (reflect.StructField, bool) {
|
||||
// First check all structs fields.
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
// Search all fields, even embedded structs.
|
||||
if 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 StructFields(t reflect.Type) map[string]Nature {
|
||||
table := make(map[string]Nature)
|
||||
|
||||
t = deref.Type(t)
|
||||
if t == nil {
|
||||
return table
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
|
||||
if f.Anonymous {
|
||||
for name, typ := range StructFields(f.Type) {
|
||||
if _, ok := table[name]; ok {
|
||||
continue
|
||||
}
|
||||
typ.FieldIndex = append(f.Index, typ.FieldIndex...)
|
||||
table[name] = typ
|
||||
}
|
||||
}
|
||||
|
||||
table[fieldName(f)] = Nature{
|
||||
Type: f.Type,
|
||||
FieldIndex: f.Index,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
190
vendor/github.com/expr-lang/expr/checker/types.go
generated
vendored
Normal file
190
vendor/github.com/expr-lang/expr/checker/types.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
package checker
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
. "github.com/expr-lang/expr/checker/nature"
|
||||
)
|
||||
|
||||
var (
|
||||
unknown = Nature{}
|
||||
nilNature = Nature{Nil: true}
|
||||
boolNature = Nature{Type: reflect.TypeOf(true)}
|
||||
integerNature = Nature{Type: reflect.TypeOf(0)}
|
||||
floatNature = Nature{Type: reflect.TypeOf(float64(0))}
|
||||
stringNature = Nature{Type: reflect.TypeOf("")}
|
||||
arrayNature = Nature{Type: reflect.TypeOf([]any{})}
|
||||
mapNature = Nature{Type: reflect.TypeOf(map[string]any{})}
|
||||
timeNature = Nature{Type: reflect.TypeOf(time.Time{})}
|
||||
durationNature = Nature{Type: reflect.TypeOf(time.Duration(0))}
|
||||
)
|
||||
|
||||
var (
|
||||
anyType = reflect.TypeOf(new(any)).Elem()
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
durationType = reflect.TypeOf(time.Duration(0))
|
||||
arrayType = reflect.TypeOf([]any{})
|
||||
)
|
||||
|
||||
func arrayOf(nt Nature) Nature {
|
||||
return Nature{
|
||||
Type: arrayType,
|
||||
ArrayOf: &nt,
|
||||
}
|
||||
}
|
||||
|
||||
func isNil(nt Nature) bool {
|
||||
return nt.Nil
|
||||
}
|
||||
|
||||
func combined(l, r Nature) Nature {
|
||||
if isUnknown(l) || isUnknown(r) {
|
||||
return unknown
|
||||
}
|
||||
if isFloat(l) || isFloat(r) {
|
||||
return floatNature
|
||||
}
|
||||
return integerNature
|
||||
}
|
||||
|
||||
func anyOf(nt Nature, fns ...func(Nature) bool) bool {
|
||||
for _, fn := range fns {
|
||||
if fn(nt) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func or(l, r Nature, fns ...func(Nature) bool) bool {
|
||||
if isUnknown(l) && isUnknown(r) {
|
||||
return true
|
||||
}
|
||||
if isUnknown(l) && anyOf(r, fns...) {
|
||||
return true
|
||||
}
|
||||
if isUnknown(r) && anyOf(l, fns...) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isUnknown(nt Nature) bool {
|
||||
return nt.IsUnknown()
|
||||
}
|
||||
|
||||
func isInteger(nt Nature) bool {
|
||||
switch nt.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 nt.PkgPath() == ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isFloat(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return nt.PkgPath() == ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isNumber(nt Nature) bool {
|
||||
return isInteger(nt) || isFloat(nt)
|
||||
}
|
||||
|
||||
func isTime(nt Nature) bool {
|
||||
switch nt.Type {
|
||||
case timeType:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isDuration(nt Nature) bool {
|
||||
switch nt.Type {
|
||||
case durationType:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isBool(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.Bool:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isString(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.String:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isArray(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isMap(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.Map:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isStruct(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isFunc(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.Func:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func kind(t reflect.Type) reflect.Kind {
|
||||
if t == nil {
|
||||
return reflect.Invalid
|
||||
}
|
||||
return t.Kind()
|
||||
}
|
||||
|
||||
func isComparable(l, r Nature) bool {
|
||||
if isUnknown(l) || isUnknown(r) {
|
||||
return true
|
||||
}
|
||||
if isNil(l) || isNil(r) {
|
||||
return true
|
||||
}
|
||||
if isNumber(l) && isNumber(r) {
|
||||
return true
|
||||
}
|
||||
if isDuration(l) && isDuration(r) {
|
||||
return true
|
||||
}
|
||||
if isTime(l) && isTime(r) {
|
||||
return true
|
||||
}
|
||||
if isArray(l) && isArray(r) {
|
||||
return true
|
||||
}
|
||||
return l.AssignableTo(r)
|
||||
}
|
1280
vendor/github.com/expr-lang/expr/compiler/compiler.go
generated
vendored
Normal file
1280
vendor/github.com/expr-lang/expr/compiler/compiler.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
101
vendor/github.com/expr-lang/expr/conf/config.go
generated
vendored
Normal file
101
vendor/github.com/expr-lang/expr/conf/config.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/expr-lang/expr/ast"
|
||||
"github.com/expr-lang/expr/builtin"
|
||||
"github.com/expr-lang/expr/checker/nature"
|
||||
"github.com/expr-lang/expr/vm/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultMemoryBudget represents an upper limit of memory usage
|
||||
DefaultMemoryBudget uint = 1e6
|
||||
|
||||
// DefaultMaxNodes represents an upper limit of AST nodes
|
||||
DefaultMaxNodes uint = 10000
|
||||
)
|
||||
|
||||
type FunctionsTable map[string]*builtin.Function
|
||||
|
||||
type Config struct {
|
||||
EnvObject any
|
||||
Env nature.Nature
|
||||
Expect reflect.Kind
|
||||
ExpectAny bool
|
||||
Optimize bool
|
||||
Strict bool
|
||||
Profile bool
|
||||
MaxNodes uint
|
||||
MemoryBudget uint
|
||||
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,
|
||||
MaxNodes: DefaultMaxNodes,
|
||||
MemoryBudget: DefaultMemoryBudget,
|
||||
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) {
|
||||
c.EnvObject = env
|
||||
c.Env = Env(env)
|
||||
c.Strict = c.Env.Strict
|
||||
}
|
||||
|
||||
func (c *Config) ConstExpr(name string) {
|
||||
if c.EnvObject == nil {
|
||||
panic("no environment is specified for ConstExpr()")
|
||||
}
|
||||
fn := reflect.ValueOf(runtime.Fetch(c.EnvObject, 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.Env.Get(name); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
68
vendor/github.com/expr-lang/expr/conf/env.go
generated
vendored
Normal file
68
vendor/github.com/expr-lang/expr/conf/env.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
. "github.com/expr-lang/expr/checker/nature"
|
||||
"github.com/expr-lang/expr/internal/deref"
|
||||
"github.com/expr-lang/expr/types"
|
||||
)
|
||||
|
||||
func Env(env any) Nature {
|
||||
if env == nil {
|
||||
return Nature{
|
||||
Type: reflect.TypeOf(map[string]any{}),
|
||||
Strict: true,
|
||||
}
|
||||
}
|
||||
|
||||
switch env := env.(type) {
|
||||
case types.Map:
|
||||
return env.Nature()
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(env)
|
||||
d := deref.Value(v)
|
||||
|
||||
switch d.Kind() {
|
||||
case reflect.Struct:
|
||||
return Nature{
|
||||
Type: v.Type(),
|
||||
Strict: true,
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
n := Nature{
|
||||
Type: v.Type(),
|
||||
Fields: make(map[string]Nature, v.Len()),
|
||||
Strict: true,
|
||||
}
|
||||
|
||||
for _, key := range v.MapKeys() {
|
||||
elem := v.MapIndex(key)
|
||||
if !elem.IsValid() || !elem.CanInterface() {
|
||||
panic(fmt.Sprintf("invalid map value: %s", key))
|
||||
}
|
||||
|
||||
face := elem.Interface()
|
||||
|
||||
switch face := face.(type) {
|
||||
case types.Map:
|
||||
n.Fields[key.String()] = face.Nature()
|
||||
|
||||
default:
|
||||
if face == nil {
|
||||
n.Fields[key.String()] = Nature{Nil: true}
|
||||
continue
|
||||
}
|
||||
n.Fields[key.String()] = Nature{Type: reflect.TypeOf(face)}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unknown type %T", env))
|
||||
}
|
260
vendor/github.com/expr-lang/expr/expr.go
generated
vendored
Normal file
260
vendor/github.com/expr-lang/expr/expr.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
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/parser"
|
||||
"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,
|
||||
Env: &c.Env,
|
||||
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")
|
||||
}
|
||||
|
||||
tree, err := parser.Parse(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
program, err := compiler.Compile(tree, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := Run(program, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
81
vendor/github.com/expr-lang/expr/file/error.go
generated
vendored
Normal file
81
vendor/github.com/expr-lang/expr/file/error.go
generated
vendored
Normal file
@@ -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,
|
||||
)
|
||||
}
|
6
vendor/github.com/expr-lang/expr/file/location.go
generated
vendored
Normal file
6
vendor/github.com/expr-lang/expr/file/location.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package file
|
||||
|
||||
type Location struct {
|
||||
From int `json:"from"`
|
||||
To int `json:"to"`
|
||||
}
|
48
vendor/github.com/expr-lang/expr/file/source.go
generated
vendored
Normal file
48
vendor/github.com/expr-lang/expr/file/source.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
47
vendor/github.com/expr-lang/expr/internal/deref/deref.go
generated
vendored
Normal file
47
vendor/github.com/expr-lang/expr/internal/deref/deref.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package deref
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func Interface(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
|
||||
}
|
81
vendor/github.com/expr-lang/expr/optimizer/const_expr.go
generated
vendored
Normal file
81
vendor/github.com/expr-lang/expr/optimizer/const_expr.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
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}
|
||||
patchWithType(node, constNode)
|
||||
c.applied = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
vendor/github.com/expr-lang/expr/optimizer/filter_first.go
generated
vendored
Normal file
38
vendor/github.com/expr-lang/expr/optimizer/filter_first.go
generated
vendored
Normal file
@@ -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 {
|
||||
patchCopyType(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 {
|
||||
patchCopyType(node, &BuiltinNode{
|
||||
Name: "find",
|
||||
Arguments: filter.Arguments,
|
||||
Throws: false, // as first() will return nil if not found
|
||||
Map: filter.Map,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
38
vendor/github.com/expr-lang/expr/optimizer/filter_last.go
generated
vendored
Normal file
38
vendor/github.com/expr-lang/expr/optimizer/filter_last.go
generated
vendored
Normal file
@@ -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 {
|
||||
patchCopyType(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 {
|
||||
patchCopyType(node, &BuiltinNode{
|
||||
Name: "findLast",
|
||||
Arguments: filter.Arguments,
|
||||
Throws: false, // as last() will return nil if not found
|
||||
Map: filter.Map,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
22
vendor/github.com/expr-lang/expr/optimizer/filter_len.go
generated
vendored
Normal file
22
vendor/github.com/expr-lang/expr/optimizer/filter_len.go
generated
vendored
Normal file
@@ -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 {
|
||||
patchCopyType(node, &BuiltinNode{
|
||||
Name: "count",
|
||||
Arguments: filter.Arguments,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
33
vendor/github.com/expr-lang/expr/optimizer/filter_map.go
generated
vendored
Normal file
33
vendor/github.com/expr-lang/expr/optimizer/filter_map.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
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 &&
|
||||
Find(mapBuiltin.Arguments[1], isIndexPointer) == nil {
|
||||
if predicate, ok := mapBuiltin.Arguments[1].(*PredicateNode); ok {
|
||||
if filter, ok := mapBuiltin.Arguments[0].(*BuiltinNode); ok &&
|
||||
filter.Name == "filter" &&
|
||||
filter.Map == nil /* not already optimized */ {
|
||||
patchCopyType(node, &BuiltinNode{
|
||||
Name: "filter",
|
||||
Arguments: filter.Arguments,
|
||||
Map: predicate.Node,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isIndexPointer(node Node) bool {
|
||||
if pointer, ok := node.(*PointerNode); ok && pointer.Name == "index" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
341
vendor/github.com/expr-lang/expr/optimizer/fold.go
generated
vendored
Normal file
341
vendor/github.com/expr-lang/expr/optimizer/fold.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
package optimizer
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
. "github.com/expr-lang/expr/ast"
|
||||
"github.com/expr-lang/expr/file"
|
||||
)
|
||||
|
||||
type fold struct {
|
||||
applied bool
|
||||
err *file.Error
|
||||
}
|
||||
|
||||
func (fold *fold) Visit(node *Node) {
|
||||
patch := func(newNode Node) {
|
||||
fold.applied = true
|
||||
patchWithType(node, newNode)
|
||||
}
|
||||
patchCopy := func(newNode Node) {
|
||||
fold.applied = true
|
||||
patchCopyType(node, newNode)
|
||||
}
|
||||
|
||||
switch n := (*node).(type) {
|
||||
case *UnaryNode:
|
||||
switch n.Operator {
|
||||
case "-":
|
||||
if i, ok := n.Node.(*IntegerNode); ok {
|
||||
patch(&IntegerNode{Value: -i.Value})
|
||||
}
|
||||
if i, ok := n.Node.(*FloatNode); ok {
|
||||
patch(&FloatNode{Value: -i.Value})
|
||||
}
|
||||
case "+":
|
||||
if i, ok := n.Node.(*IntegerNode); ok {
|
||||
patch(&IntegerNode{Value: i.Value})
|
||||
}
|
||||
if i, ok := n.Node.(*FloatNode); ok {
|
||||
patch(&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 {
|
||||
patch(&IntegerNode{Value: a.Value + b.Value})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toInteger(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: float64(a.Value) + b.Value})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toInteger(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: a.Value + float64(b.Value)})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&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 {
|
||||
patch(&IntegerNode{Value: a.Value - b.Value})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toInteger(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: float64(a.Value) - b.Value})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toInteger(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: a.Value - float64(b.Value)})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: a.Value - b.Value})
|
||||
}
|
||||
}
|
||||
case "*":
|
||||
{
|
||||
a := toInteger(n.Left)
|
||||
b := toInteger(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&IntegerNode{Value: a.Value * b.Value})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toInteger(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: float64(a.Value) * b.Value})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toInteger(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: a.Value * float64(b.Value)})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: a.Value * b.Value})
|
||||
}
|
||||
}
|
||||
case "/":
|
||||
{
|
||||
a := toInteger(n.Left)
|
||||
b := toInteger(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: float64(a.Value) / float64(b.Value)})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toInteger(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: float64(a.Value) / b.Value})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toInteger(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: a.Value / float64(b.Value)})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&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 {
|
||||
patch(&FloatNode{Value: math.Pow(float64(a.Value), float64(b.Value))})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toInteger(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: math.Pow(float64(a.Value), b.Value)})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toInteger(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&FloatNode{Value: math.Pow(a.Value, float64(b.Value))})
|
||||
}
|
||||
}
|
||||
{
|
||||
a := toFloat(n.Left)
|
||||
b := toFloat(n.Right)
|
||||
if a != nil && b != nil {
|
||||
patch(&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
|
||||
patchCopy(n.Right)
|
||||
} else if b != nil && b.Value { // x and true
|
||||
patchCopy(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
|
||||
patchCopy(n.Right)
|
||||
} else if b != nil && !b.Value { // x or false
|
||||
patchCopy(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:
|
||||
// TODO: Move this to a separate visitor filter_filter.go
|
||||
switch n.Name {
|
||||
case "filter":
|
||||
if len(n.Arguments) != 2 {
|
||||
return
|
||||
}
|
||||
if base, ok := n.Arguments[0].(*BuiltinNode); ok && base.Name == "filter" {
|
||||
patchCopy(&BuiltinNode{
|
||||
Name: "filter",
|
||||
Arguments: []Node{
|
||||
base.Arguments[0],
|
||||
&BinaryNode{
|
||||
Operator: "&&",
|
||||
Left: base.Arguments[1].(*PredicateNode).Node,
|
||||
Right: n.Arguments[1].(*PredicateNode).Node,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
68
vendor/github.com/expr-lang/expr/optimizer/in_array.go
generated
vendored
Normal file
68
vendor/github.com/expr-lang/expr/optimizer/in_array.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
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{}{}
|
||||
}
|
||||
m := &ConstantNode{Value: value}
|
||||
m.SetType(reflect.TypeOf(value))
|
||||
patchCopyType(node, &BinaryNode{
|
||||
Operator: n.Operator,
|
||||
Left: n.Left,
|
||||
Right: m,
|
||||
})
|
||||
}
|
||||
|
||||
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{}{}
|
||||
}
|
||||
m := &ConstantNode{Value: value}
|
||||
m.SetType(reflect.TypeOf(value))
|
||||
patchCopyType(node, &BinaryNode{
|
||||
Operator: n.Operator,
|
||||
Left: n.Left,
|
||||
Right: m,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
vendor/github.com/expr-lang/expr/optimizer/in_range.go
generated
vendored
Normal file
43
vendor/github.com/expr-lang/expr/optimizer/in_range.go
generated
vendored
Normal file
@@ -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 {
|
||||
patchCopyType(node, &BinaryNode{
|
||||
Operator: "and",
|
||||
Left: &BinaryNode{
|
||||
Operator: ">=",
|
||||
Left: n.Left,
|
||||
Right: from,
|
||||
},
|
||||
Right: &BinaryNode{
|
||||
Operator: "<=",
|
||||
Left: n.Left,
|
||||
Right: to,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
vendor/github.com/expr-lang/expr/optimizer/optimizer.go
generated
vendored
Normal file
79
vendor/github.com/expr-lang/expr/optimizer/optimizer.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package optimizer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
. "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
|
||||
}
|
||||
|
||||
var (
|
||||
boolType = reflect.TypeOf(true)
|
||||
integerType = reflect.TypeOf(0)
|
||||
floatType = reflect.TypeOf(float64(0))
|
||||
stringType = reflect.TypeOf("")
|
||||
)
|
||||
|
||||
func patchWithType(node *Node, newNode Node) {
|
||||
switch n := newNode.(type) {
|
||||
case *BoolNode:
|
||||
newNode.SetType(boolType)
|
||||
case *IntegerNode:
|
||||
newNode.SetType(integerType)
|
||||
case *FloatNode:
|
||||
newNode.SetType(floatType)
|
||||
case *StringNode:
|
||||
newNode.SetType(stringType)
|
||||
case *ConstantNode:
|
||||
newNode.SetType(reflect.TypeOf(n.Value))
|
||||
case *BinaryNode:
|
||||
newNode.SetType(n.Type())
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown type %T", newNode))
|
||||
}
|
||||
Patch(node, newNode)
|
||||
}
|
||||
|
||||
func patchCopyType(node *Node, newNode Node) {
|
||||
t := (*node).Type()
|
||||
newNode.SetType(t)
|
||||
Patch(node, newNode)
|
||||
}
|
61
vendor/github.com/expr-lang/expr/optimizer/predicate_combination.go
generated
vendored
Normal file
61
vendor/github.com/expr-lang/expr/optimizer/predicate_combination.go
generated
vendored
Normal file
@@ -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() {
|
||||
predicate := &PredicateNode{
|
||||
Node: &BinaryNode{
|
||||
Operator: combinedOp,
|
||||
Left: left.Arguments[1].(*PredicateNode).Node,
|
||||
Right: right.Arguments[1].(*PredicateNode).Node,
|
||||
},
|
||||
}
|
||||
v.Visit(&predicate.Node)
|
||||
patchCopyType(node, &BuiltinNode{
|
||||
Name: left.Name,
|
||||
Arguments: []Node{
|
||||
left.Arguments[0],
|
||||
predicate,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
37
vendor/github.com/expr-lang/expr/optimizer/sum_array.go
generated
vendored
Normal file
37
vendor/github.com/expr-lang/expr/optimizer/sum_array.go
generated
vendored
Normal file
@@ -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 {
|
||||
patchCopyType(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)))
|
||||
}
|
25
vendor/github.com/expr-lang/expr/optimizer/sum_map.go
generated
vendored
Normal file
25
vendor/github.com/expr-lang/expr/optimizer/sum_map.go
generated
vendored
Normal file
@@ -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 {
|
||||
patchCopyType(node, &BuiltinNode{
|
||||
Name: "sum",
|
||||
Arguments: []Node{
|
||||
mapBuiltin.Arguments[0],
|
||||
mapBuiltin.Arguments[1],
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
230
vendor/github.com/expr-lang/expr/parser/lexer/lexer.go
generated
vendored
Normal file
230
vendor/github.com/expr-lang/expr/parser/lexer/lexer.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
226
vendor/github.com/expr-lang/expr/parser/lexer/state.go
generated
vendored
Normal file
226
vendor/github.com/expr-lang/expr/parser/lexer/state.go
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
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", "let", "if", "else":
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
47
vendor/github.com/expr-lang/expr/parser/lexer/token.go
generated
vendored
Normal file
47
vendor/github.com/expr-lang/expr/parser/lexer/token.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
186
vendor/github.com/expr-lang/expr/parser/lexer/utils.go
generated
vendored
Normal file
186
vendor/github.com/expr-lang/expr/parser/lexer/utils.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
69
vendor/github.com/expr-lang/expr/parser/operator/operator.go
generated
vendored
Normal file
69
vendor/github.com/expr-lang/expr/parser/operator/operator.go
generated
vendored
Normal file
@@ -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 == "<="
|
||||
}
|
891
vendor/github.com/expr-lang/expr/parser/parser.go
generated
vendored
Normal file
891
vendor/github.com/expr-lang/expr/parser/parser.go
generated
vendored
Normal file
@@ -0,0 +1,891 @@
|
||||
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
|
||||
predicate
|
||||
)
|
||||
|
||||
const optional arg = 1 << 7
|
||||
|
||||
var predicates = map[string]struct {
|
||||
args []arg
|
||||
}{
|
||||
"all": {[]arg{expr, predicate}},
|
||||
"none": {[]arg{expr, predicate}},
|
||||
"any": {[]arg{expr, predicate}},
|
||||
"one": {[]arg{expr, predicate}},
|
||||
"filter": {[]arg{expr, predicate}},
|
||||
"map": {[]arg{expr, predicate}},
|
||||
"count": {[]arg{expr, predicate | optional}},
|
||||
"sum": {[]arg{expr, predicate | optional}},
|
||||
"find": {[]arg{expr, predicate}},
|
||||
"findIndex": {[]arg{expr, predicate}},
|
||||
"findLast": {[]arg{expr, predicate}},
|
||||
"findLastIndex": {[]arg{expr, predicate}},
|
||||
"groupBy": {[]arg{expr, predicate}},
|
||||
"sortBy": {[]arg{expr, predicate, expr | optional}},
|
||||
"reduce": {[]arg{expr, predicate, expr | optional}},
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
tokens []Token
|
||||
current Token
|
||||
pos int
|
||||
err *file.Error
|
||||
config *conf.Config
|
||||
depth int // predicate call depth
|
||||
nodeCount uint // tracks number of AST nodes created
|
||||
}
|
||||
|
||||
func (p *parser) checkNodeLimit() error {
|
||||
p.nodeCount++
|
||||
if p.config == nil {
|
||||
if p.nodeCount > conf.DefaultMaxNodes {
|
||||
p.error("compilation failed: expression exceeds maximum allowed nodes")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if p.config.MaxNodes > 0 && p.nodeCount > p.config.MaxNodes {
|
||||
p.error("compilation failed: expression exceeds maximum allowed nodes")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *parser) createNode(n Node, loc file.Location) Node {
|
||||
if err := p.checkNodeLimit(); err != nil {
|
||||
return nil
|
||||
}
|
||||
if n == nil || p.err != nil {
|
||||
return nil
|
||||
}
|
||||
n.SetLocation(loc)
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) createMemberNode(n *MemberNode, loc file.Location) *MemberNode {
|
||||
if err := p.checkNodeLimit(); err != nil {
|
||||
return nil
|
||||
}
|
||||
if n == nil || p.err != nil {
|
||||
return nil
|
||||
}
|
||||
n.SetLocation(loc)
|
||||
return n
|
||||
}
|
||||
|
||||
type Tree struct {
|
||||
Node Node
|
||||
Source file.Source
|
||||
}
|
||||
|
||||
func Parse(input string) (*Tree, error) {
|
||||
return ParseWithConfig(input, nil)
|
||||
}
|
||||
|
||||
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.parseSequenceExpression()
|
||||
|
||||
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) parseSequenceExpression() Node {
|
||||
nodes := []Node{p.parseExpression(0)}
|
||||
|
||||
for p.current.Is(Operator, ";") && p.err == nil {
|
||||
p.next()
|
||||
// If a trailing semicolon is present, break out.
|
||||
if p.current.Is(EOF) {
|
||||
break
|
||||
}
|
||||
nodes = append(nodes, p.parseExpression(0))
|
||||
}
|
||||
|
||||
if len(nodes) == 1 {
|
||||
return nodes[0]
|
||||
}
|
||||
|
||||
return p.createNode(&SequenceNode{
|
||||
Nodes: nodes,
|
||||
}, nodes[0].Location())
|
||||
}
|
||||
|
||||
func (p *parser) parseExpression(precedence int) Node {
|
||||
if p.err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if precedence == 0 && p.current.Is(Operator, "let") {
|
||||
return p.parseVariableDeclaration()
|
||||
}
|
||||
|
||||
if precedence == 0 && p.current.Is(Operator, "if") {
|
||||
return p.parseConditionalIf()
|
||||
}
|
||||
|
||||
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 = p.createNode(&BinaryNode{
|
||||
Operator: opToken.Value,
|
||||
Left: nodeLeft,
|
||||
Right: nodeRight,
|
||||
}, opToken.Location)
|
||||
if nodeLeft == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if negate {
|
||||
nodeLeft = p.createNode(&UnaryNode{
|
||||
Operator: "not",
|
||||
Node: nodeLeft,
|
||||
}, notToken.Location)
|
||||
if nodeLeft == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
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.parseSequenceExpression()
|
||||
return p.createNode(&VariableDeclaratorNode{
|
||||
Name: variableName.Value,
|
||||
Value: value,
|
||||
Expr: node,
|
||||
}, variableName.Location)
|
||||
}
|
||||
|
||||
func (p *parser) parseConditionalIf() Node {
|
||||
p.next()
|
||||
nodeCondition := p.parseExpression(0)
|
||||
p.expect(Bracket, "{")
|
||||
expr1 := p.parseSequenceExpression()
|
||||
p.expect(Bracket, "}")
|
||||
p.expect(Operator, "else")
|
||||
p.expect(Bracket, "{")
|
||||
expr2 := p.parseSequenceExpression()
|
||||
p.expect(Bracket, "}")
|
||||
|
||||
return &ConditionalNode{
|
||||
Cond: nodeCondition,
|
||||
Exp1: expr1,
|
||||
Exp2: expr2,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 = p.createNode(&ConditionalNode{
|
||||
Cond: node,
|
||||
Exp1: expr1,
|
||||
Exp2: expr2,
|
||||
}, p.current.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
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 := p.createNode(&UnaryNode{
|
||||
Operator: token.Value,
|
||||
Node: expr,
|
||||
}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return p.parsePostfixExpression(node)
|
||||
}
|
||||
}
|
||||
|
||||
if token.Is(Bracket, "(") {
|
||||
p.next()
|
||||
expr := p.parseSequenceExpression()
|
||||
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 := p.createNode(&PointerNode{Name: name}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return p.parsePostfixExpression(node)
|
||||
}
|
||||
}
|
||||
|
||||
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 = p.createNode(&BoolNode{Value: true}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return node
|
||||
case "false":
|
||||
node = p.createNode(&BoolNode{Value: false}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return node
|
||||
case "nil":
|
||||
node = p.createNode(&NilNode{}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return node
|
||||
default:
|
||||
if p.current.Is(Bracket, "(") {
|
||||
node = p.parseCall(token, []Node{}, true)
|
||||
} else {
|
||||
node = p.createNode(&IdentifierNode{Value: token.Value}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = p.createNode(&StringNode{Value: token.Value}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
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 p.createNode(&IntegerNode{Value: int(number)}, p.current.Location)
|
||||
}
|
||||
|
||||
func (p *parser) toFloatNode(number float64) Node {
|
||||
if number > math.MaxFloat64 {
|
||||
p.error("float literal is too large")
|
||||
return nil
|
||||
}
|
||||
return p.createNode(&FloatNode{Value: number}, p.current.Location)
|
||||
}
|
||||
|
||||
func (p *parser) parseCall(token Token, arguments []Node, checkOverrides bool) Node {
|
||||
var node Node
|
||||
|
||||
isOverridden := false
|
||||
if p.config != nil {
|
||||
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&predicate == predicate:
|
||||
node = p.parsePredicate()
|
||||
}
|
||||
arguments = append(arguments, node)
|
||||
}
|
||||
|
||||
// skip last comma
|
||||
if p.current.Is(Operator, ",") {
|
||||
p.next()
|
||||
}
|
||||
p.expect(Bracket, ")")
|
||||
|
||||
node = p.createNode(&BuiltinNode{
|
||||
Name: token.Value,
|
||||
Arguments: arguments,
|
||||
}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
} else if _, ok := builtin.Index[token.Value]; ok && (p.config == nil || !p.config.Disabled[token.Value]) && !isOverridden {
|
||||
node = p.createNode(&BuiltinNode{
|
||||
Name: token.Value,
|
||||
Arguments: p.parseArguments(arguments),
|
||||
}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
} else {
|
||||
callee := p.createNode(&IdentifierNode{Value: token.Value}, token.Location)
|
||||
if callee == nil {
|
||||
return nil
|
||||
}
|
||||
node = p.createNode(&CallNode{
|
||||
Callee: callee,
|
||||
Arguments: p.parseArguments(arguments),
|
||||
}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
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, ",")
|
||||
}
|
||||
if p.current.Is(Bracket, ")") {
|
||||
break
|
||||
}
|
||||
node := p.parseExpression(0)
|
||||
arguments = append(arguments, node)
|
||||
}
|
||||
p.expect(Bracket, ")")
|
||||
|
||||
return arguments
|
||||
}
|
||||
|
||||
func (p *parser) parsePredicate() Node {
|
||||
startToken := p.current
|
||||
withBrackets := false
|
||||
if p.current.Is(Bracket, "{") {
|
||||
p.next()
|
||||
withBrackets = true
|
||||
}
|
||||
|
||||
p.depth++
|
||||
var node Node
|
||||
if withBrackets {
|
||||
node = p.parseSequenceExpression()
|
||||
} else {
|
||||
node = p.parseExpression(0)
|
||||
if p.current.Is(Operator, ";") {
|
||||
p.error("wrap predicate with brackets { and }")
|
||||
}
|
||||
}
|
||||
p.depth--
|
||||
|
||||
if withBrackets {
|
||||
p.expect(Bracket, "}")
|
||||
}
|
||||
predicateNode := p.createNode(&PredicateNode{
|
||||
Node: node,
|
||||
}, startToken.Location)
|
||||
if predicateNode == nil {
|
||||
return nil
|
||||
}
|
||||
return predicateNode
|
||||
}
|
||||
|
||||
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 := p.createNode(&ArrayNode{Nodes: nodes}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
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 = p.createNode(&StringNode{Value: p.current.Value}, p.current.Location)
|
||||
if key == nil {
|
||||
return nil
|
||||
}
|
||||
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 := p.createNode(&PairNode{Key: key, Value: node}, token.Location)
|
||||
if pair == nil {
|
||||
return nil
|
||||
}
|
||||
nodes = append(nodes, pair)
|
||||
}
|
||||
|
||||
end:
|
||||
p.expect(Bracket, "}")
|
||||
|
||||
node := p.createNode(&MapNode{Pairs: nodes}, token.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
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 := p.createNode(&StringNode{Value: propertyToken.Value}, propertyToken.Location)
|
||||
if property == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
chainNode, isChain := node.(*ChainNode)
|
||||
optional := postfixToken.Value == "?."
|
||||
|
||||
if isChain {
|
||||
node = chainNode.Node
|
||||
}
|
||||
|
||||
memberNode := p.createMemberNode(&MemberNode{
|
||||
Node: node,
|
||||
Property: property,
|
||||
Optional: optional,
|
||||
}, propertyToken.Location)
|
||||
if memberNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.current.Is(Bracket, "(") {
|
||||
memberNode.Method = true
|
||||
node = p.createNode(&CallNode{
|
||||
Callee: memberNode,
|
||||
Arguments: p.parseArguments([]Node{}),
|
||||
}, propertyToken.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
node = memberNode
|
||||
}
|
||||
|
||||
if isChain || optional {
|
||||
node = p.createNode(&ChainNode{Node: node}, propertyToken.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
} 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 = p.createNode(&SliceNode{
|
||||
Node: node,
|
||||
To: to,
|
||||
}, postfixToken.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
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 = p.createNode(&SliceNode{
|
||||
Node: node,
|
||||
From: from,
|
||||
To: to,
|
||||
}, postfixToken.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
p.expect(Bracket, "]")
|
||||
|
||||
} else {
|
||||
// Slice operator [:] was not found,
|
||||
// it should be just an index node.
|
||||
node = p.createNode(&MemberNode{
|
||||
Node: node,
|
||||
Property: from,
|
||||
Optional: optional,
|
||||
}, postfixToken.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
if optional {
|
||||
node = p.createNode(&ChainNode{Node: node}, postfixToken.Location)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
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 := p.createNode(&BinaryNode{
|
||||
Operator: token.Value,
|
||||
Left: left,
|
||||
Right: comparator,
|
||||
}, token.Location)
|
||||
if cmpNode == nil {
|
||||
return nil
|
||||
}
|
||||
if rootNode == nil {
|
||||
rootNode = cmpNode
|
||||
} else {
|
||||
rootNode = p.createNode(&BinaryNode{
|
||||
Operator: "&&",
|
||||
Left: rootNode,
|
||||
Right: cmpNode,
|
||||
}, token.Location)
|
||||
if rootNode == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
left = comparator
|
||||
token = p.current
|
||||
if !(token.Is(Operator) && operator.IsComparison(token.Value) && p.err == nil) {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
return rootNode
|
||||
}
|
34
vendor/github.com/expr-lang/expr/parser/utils/utils.go
generated
vendored
Normal file
34
vendor/github.com/expr-lang/expr/parser/utils/utils.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
147
vendor/github.com/expr-lang/expr/patcher/operator_override.go
generated
vendored
Normal file
147
vendor/github.com/expr-lang/expr/patcher/operator_override.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package patcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/expr-lang/expr/ast"
|
||||
"github.com/expr-lang/expr/builtin"
|
||||
"github.com/expr-lang/expr/checker/nature"
|
||||
"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.
|
||||
Env *nature.Nature // Env type.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Tracking must be reset before every walk over the AST tree
|
||||
func (p *OperatorOverloading) Reset() {
|
||||
p.applied = false
|
||||
}
|
||||
|
||||
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.Env.Get(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.Env.Get(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 nature.Nature, 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))
|
||||
}
|
||||
}
|
||||
}
|
45
vendor/github.com/expr-lang/expr/patcher/with_context.go
generated
vendored
Normal file
45
vendor/github.com/expr-lang/expr/patcher/with_context.go
generated
vendored
Normal file
@@ -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...),
|
||||
})
|
||||
}
|
||||
}
|
25
vendor/github.com/expr-lang/expr/patcher/with_timezone.go
generated
vendored
Normal file
25
vendor/github.com/expr-lang/expr/patcher/with_timezone.go
generated
vendored
Normal file
@@ -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...),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
181
vendor/github.com/expr-lang/expr/types/types.go
generated
vendored
Normal file
181
vendor/github.com/expr-lang/expr/types/types.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
. "github.com/expr-lang/expr/checker/nature"
|
||||
)
|
||||
|
||||
// Type is a type that can be used to represent a value.
|
||||
type Type interface {
|
||||
Nature() Nature
|
||||
Equal(Type) bool
|
||||
String() string
|
||||
}
|
||||
|
||||
var (
|
||||
Int = TypeOf(0)
|
||||
Int8 = TypeOf(int8(0))
|
||||
Int16 = TypeOf(int16(0))
|
||||
Int32 = TypeOf(int32(0))
|
||||
Int64 = TypeOf(int64(0))
|
||||
Uint = TypeOf(uint(0))
|
||||
Uint8 = TypeOf(uint8(0))
|
||||
Uint16 = TypeOf(uint16(0))
|
||||
Uint32 = TypeOf(uint32(0))
|
||||
Uint64 = TypeOf(uint64(0))
|
||||
Float = TypeOf(float32(0))
|
||||
Float64 = TypeOf(float64(0))
|
||||
String = TypeOf("")
|
||||
Bool = TypeOf(true)
|
||||
Nil = nilType{}
|
||||
Any = anyType{}
|
||||
)
|
||||
|
||||
func TypeOf(v any) Type {
|
||||
if v == nil {
|
||||
return Nil
|
||||
}
|
||||
return rtype{t: reflect.TypeOf(v)}
|
||||
}
|
||||
|
||||
type anyType struct{}
|
||||
|
||||
func (anyType) Nature() Nature {
|
||||
return Nature{Type: nil}
|
||||
}
|
||||
|
||||
func (anyType) Equal(t Type) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (anyType) String() string {
|
||||
return "any"
|
||||
}
|
||||
|
||||
type nilType struct{}
|
||||
|
||||
func (nilType) Nature() Nature {
|
||||
return Nature{Nil: true}
|
||||
}
|
||||
|
||||
func (nilType) Equal(t Type) bool {
|
||||
if t == Any {
|
||||
return true
|
||||
}
|
||||
return t == Nil
|
||||
}
|
||||
|
||||
func (nilType) String() string {
|
||||
return "nil"
|
||||
}
|
||||
|
||||
type rtype struct {
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
func (r rtype) Nature() Nature {
|
||||
return Nature{Type: r.t}
|
||||
}
|
||||
|
||||
func (r rtype) Equal(t Type) bool {
|
||||
if t == Any {
|
||||
return true
|
||||
}
|
||||
if rt, ok := t.(rtype); ok {
|
||||
return r.t.String() == rt.t.String()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r rtype) String() string {
|
||||
return r.t.String()
|
||||
}
|
||||
|
||||
// Map represents a map[string]any type with defined keys.
|
||||
type Map map[string]Type
|
||||
|
||||
const Extra = "[[__extra_keys__]]"
|
||||
|
||||
func (m Map) Nature() Nature {
|
||||
nt := Nature{
|
||||
Type: reflect.TypeOf(map[string]any{}),
|
||||
Fields: make(map[string]Nature, len(m)),
|
||||
Strict: true,
|
||||
}
|
||||
for k, v := range m {
|
||||
if k == Extra {
|
||||
nt.Strict = false
|
||||
natureOfDefaultValue := v.Nature()
|
||||
nt.DefaultMapValue = &natureOfDefaultValue
|
||||
continue
|
||||
}
|
||||
nt.Fields[k] = v.Nature()
|
||||
}
|
||||
return nt
|
||||
}
|
||||
|
||||
func (m Map) Equal(t Type) bool {
|
||||
if t == Any {
|
||||
return true
|
||||
}
|
||||
mt, ok := t.(Map)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if len(m) != len(mt) {
|
||||
return false
|
||||
}
|
||||
for k, v := range m {
|
||||
if !v.Equal(mt[k]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m Map) String() string {
|
||||
pairs := make([]string, 0, len(m))
|
||||
for k, v := range m {
|
||||
pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String()))
|
||||
}
|
||||
return fmt.Sprintf("Map{%s}", strings.Join(pairs, ", "))
|
||||
}
|
||||
|
||||
// Array returns a type that represents an array of the given type.
|
||||
func Array(of Type) Type {
|
||||
return array{of}
|
||||
}
|
||||
|
||||
type array struct {
|
||||
of Type
|
||||
}
|
||||
|
||||
func (a array) Nature() Nature {
|
||||
of := a.of.Nature()
|
||||
return Nature{
|
||||
Type: reflect.TypeOf([]any{}),
|
||||
Fields: make(map[string]Nature, 1),
|
||||
ArrayOf: &of,
|
||||
}
|
||||
}
|
||||
|
||||
func (a array) Equal(t Type) bool {
|
||||
if t == Any {
|
||||
return true
|
||||
}
|
||||
at, ok := t.(array)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if a.of.Equal(at.of) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a array) String() string {
|
||||
return fmt.Sprintf("Array{%s}", a.of.String())
|
||||
}
|
6
vendor/github.com/expr-lang/expr/vm/debug.go
generated
vendored
Normal file
6
vendor/github.com/expr-lang/expr/vm/debug.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
//go:build expr_debug
|
||||
// +build expr_debug
|
||||
|
||||
package vm
|
||||
|
||||
const debug = true
|
6
vendor/github.com/expr-lang/expr/vm/debug_off.go
generated
vendored
Normal file
6
vendor/github.com/expr-lang/expr/vm/debug_off.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
//go:build !expr_debug
|
||||
// +build !expr_debug
|
||||
|
||||
package vm
|
||||
|
||||
const debug = false
|
370
vendor/github.com/expr-lang/expr/vm/func_types[generated].go
generated
vendored
Normal file
370
vendor/github.com/expr-lang/expr/vm/func_types[generated].go
generated
vendored
Normal file
@@ -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))
|
||||
}
|
88
vendor/github.com/expr-lang/expr/vm/opcodes.go
generated
vendored
Normal file
88
vendor/github.com/expr-lang/expr/vm/opcodes.go
generated
vendored
Normal file
@@ -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.
|
||||
)
|
382
vendor/github.com/expr-lang/expr/vm/program.go
generated
vendored
Normal file
382
vendor/github.com/expr-lang/expr/vm/program.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
3718
vendor/github.com/expr-lang/expr/vm/runtime/helpers[generated].go
generated
vendored
Normal file
3718
vendor/github.com/expr-lang/expr/vm/runtime/helpers[generated].go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
394
vendor/github.com/expr-lang/expr/vm/runtime/runtime.go
generated
vendored
Normal file
394
vendor/github.com/expr-lang/expr/vm/runtime/runtime.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
||||
}
|
45
vendor/github.com/expr-lang/expr/vm/runtime/sort.go
generated
vendored
Normal file
45
vendor/github.com/expr-lang/expr/vm/runtime/sort.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
37
vendor/github.com/expr-lang/expr/vm/utils.go
generated
vendored
Normal file
37
vendor/github.com/expr-lang/expr/vm/utils.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
Function = func(params ...any) (any, error)
|
||||
SafeFunction = func(params ...any) (any, uint, error)
|
||||
)
|
||||
|
||||
var (
|
||||
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
|
||||
}
|
617
vendor/github.com/expr-lang/expr/vm/vm.go
generated
vendored
Normal file
617
vendor/github.com/expr-lang/expr/vm/vm.go
generated
vendored
Normal file
@@ -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/conf"
|
||||
"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
|
||||
MemoryBudget uint
|
||||
ip int
|
||||
memory 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)
|
||||
}
|
||||
|
||||
if vm.MemoryBudget == 0 {
|
||||
vm.MemoryBudget = conf.DefaultMemoryBudget
|
||||
}
|
||||
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 {
|
||||
in[i] = reflect.Zero(fn.Type().In(i))
|
||||
} 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.Interface(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
|
||||
}
|
Reference in New Issue
Block a user