snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
30
server/vendor/github.com/PaesslerAG/gval/.gitignore
generated
vendored
Normal file
30
server/vendor/github.com/PaesslerAG/gval/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
coverage.out
|
||||
|
||||
manual_test.go
|
||||
*.out
|
||||
*.err
|
||||
|
||||
.vscode
|
||||
12
server/vendor/github.com/PaesslerAG/gval/.travis.yml
generated
vendored
Normal file
12
server/vendor/github.com/PaesslerAG/gval/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
language: go
|
||||
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- go test -bench=. -benchmem -timeout 9m -coverprofile coverage.out
|
||||
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
|
||||
- go test -bench=Random -benchtime 3m -timeout 9m -benchmem -coverprofile coverage.out
|
||||
|
||||
go: "1.15"
|
||||
12
server/vendor/github.com/PaesslerAG/gval/LICENSE
generated
vendored
Normal file
12
server/vendor/github.com/PaesslerAG/gval/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Copyright (c) 2017, Paessler AG <support@paessler.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
163
server/vendor/github.com/PaesslerAG/gval/README.md
generated
vendored
Normal file
163
server/vendor/github.com/PaesslerAG/gval/README.md
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
# Gval
|
||||
|
||||
[](https://pkg.go.dev/github.com/PaesslerAG/gval)
|
||||
[](https://travis-ci.org/PaesslerAG/gval)
|
||||
[](https://coveralls.io/github/PaesslerAG/gval?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/PaesslerAG/gval)
|
||||
|
||||
Gval (Go eVALuate) provides support for evaluating arbitrary expressions, in particular Go-like expressions.
|
||||
|
||||

|
||||
|
||||
## Evaluate
|
||||
|
||||
Gval can evaluate expressions with parameters, arimethetic, logical, and string operations:
|
||||
|
||||
- basic expression: [10 > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Basic)
|
||||
- parameterized expression: [foo > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Parameter)
|
||||
- nested parameterized expression: [foo.bar > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedParameter)
|
||||
- arithmetic expression: [(requests_made * requests_succeeded / 100) >= 90](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Arithmetic)
|
||||
- string expression: [http_response_body == "service is ok"](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-String)
|
||||
- float64 expression: [(mem_used / total_mem) * 100](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Float64)
|
||||
|
||||
It can easily be extended with custom functions or operators:
|
||||
|
||||
- custom date comparator: [date(\`2014-01-02\`) > date(\`2014-01-01 23:59:59\`)](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-DateComparison)
|
||||
- string length: [strlen("someReallyLongInputString") <= 16](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Strlen)
|
||||
|
||||
You can parse gval.Expressions once and re-use them multiple times. Parsing is the compute-intensive phase of the process, so if you intend to use the same expression with different parameters, just parse it once:
|
||||
|
||||
- [Parsing and Evaluation](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluable)
|
||||
|
||||
The normal Go-standard order of operators is respected. When writing an expression, be sure that you either order the operators correctly, or use parentheses to clarify which portions of an expression should be run first.
|
||||
|
||||
Strings, numbers, and booleans can be used like in Go:
|
||||
|
||||
- [(7 < "47" == true ? "hello world!\n\u263a") + \` more text\`](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Encoding)
|
||||
|
||||
## Parameter
|
||||
|
||||
Variables can be accessed via string literals. They can be used for values with string keys if the parameter is a `map[string]interface{}` or `map[interface{}]interface{}` and for fields or methods if the parameter is a struct.
|
||||
|
||||
- [foo > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Parameter)
|
||||
|
||||
### Bracket Selector
|
||||
|
||||
Map and array elements and Struct Field can be accessed via `[]`.
|
||||
|
||||
- [foo[0]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Array)
|
||||
- [foo["b" + "a" + "r"]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-ExampleEvaluate_ComplexAccessor)
|
||||
|
||||
### Dot Selector
|
||||
|
||||
A nested variable with a name containing only letters and underscores can be accessed via a dot selector.
|
||||
|
||||
- [foo.bar > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedParameter)
|
||||
|
||||
### Custom Selector
|
||||
|
||||
Parameter names like `response-time` will be interpreted as `response` minus `time`. While gval doesn't support these parameter names directly, you can easily access them via a custom extension like [JSON Path](https://github.com/PaesslerAG/jsonpath):
|
||||
|
||||
- [$["response-time"]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Jsonpath)
|
||||
|
||||
Jsonpath is also suitable for accessing array elements.
|
||||
|
||||
### Fields and Methods
|
||||
|
||||
If you have structs in your parameters, you can access their fields and methods in the usual way:
|
||||
|
||||
- [foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-FlatAccessor)
|
||||
|
||||
It also works if the parameter is a struct directly
|
||||
[Hello + World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Accessor)
|
||||
or if the fields are nested
|
||||
[foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedAccessor)
|
||||
|
||||
This may be convenient but note that using accessors on strucs makes the expression about four times slower than just using a parameter (consult the benchmarks for more precise measurements on your system). If there are functions you want to use, it's faster (and probably cleaner) to define them as functions (see the Evaluate section). These approaches use no reflection, and are designed to be fast and clean.
|
||||
|
||||
## Default Language
|
||||
|
||||
The default language is in serveral sub languages like text, arithmetic or propositional logic defined. See [Godoc](https://pkg.go.dev/github.com/PaesslerAG/gval/#Gval) for details. All sub languages are merged into gval.Full which contains the following elements:
|
||||
|
||||
- Modifiers: `+` `-` `/` `*` `&` `|` `^` `**` `%` `>>` `<<`
|
||||
- Comparators: `>` `>=` `<` `<=` `==` `!=` `=~` `!~`
|
||||
- Logical ops: `||` `&&`
|
||||
- Numeric constants, as 64-bit floating point (`12345.678`)
|
||||
- String constants (double quotes: `"foobar"`)
|
||||
- Date function 'Date(x)', using any permutation of RFC3339, ISO8601, ruby date, or unix date
|
||||
- Boolean constants: `true` `false`
|
||||
- Parentheses to control order of evaluation `(` `)`
|
||||
- Json Arrays : `[1, 2, "foo"]`
|
||||
- Json Objects : `{"a":1, "b":2, "c":"foo"}`
|
||||
- Prefixes: `!` `-` `~`
|
||||
- Ternary conditional: `?` `:`
|
||||
- Null coalescence: `??`
|
||||
|
||||
## Customize
|
||||
|
||||
Gval is completly customizable. Every constant, function or operator can be defined separately and existing expression languages can be reused:
|
||||
|
||||
- [foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Language)
|
||||
|
||||
For details see [Godoc](https://pkg.go.dev/github.com/PaesslerAG/gval).
|
||||
|
||||
### Implementing custom selector
|
||||
|
||||
In a case you want to provide custom logic for selectors you can implement `SelectGVal(ctx context.Context, k string) (interface{}, error)` on your struct.
|
||||
Function receives next part of the path and can return any type of var that is again evaluated through standard gval procedures.
|
||||
|
||||
[Example Custom Selector](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-custom-selector)
|
||||
|
||||
### External gval Languages
|
||||
|
||||
A list of external libraries for gval. Feel free to add your own library.
|
||||
|
||||
- [gvalstrings](https://github.com/generikvault/gvalstrings) parse single quoted strings in gval.
|
||||
- [jsonpath](https://github.com/PaesslerAG/jsonpath) full support for jsonpath in gval.
|
||||
|
||||
## Performance
|
||||
|
||||
The library is built with the intention of being quick but has not been aggressively profiled and optimized. For most applications, though, it is completely fine.
|
||||
If performance is an issue, make sure to create your expression language with all functions, constants and operators only once. Evaluating an expression like gval.Evaluate("expression, const1, func1, func2, ...) creates a new gval.Language everytime it is called and slows execution.
|
||||
|
||||
The library comes with a bunch of benchmarks to measure the performance of parsing and evaluating expressions. You can run them with `go test -bench=.`.
|
||||
|
||||
For a very rough idea of performance, here are the results from a benchmark run on a Dell Latitude E7470 Win 10 i5-6300U.
|
||||
|
||||
``` text
|
||||
BenchmarkGval/const_evaluation-4 500000000 3.57 ns/op
|
||||
BenchmarkGval/const_parsing-4 1000000 1144 ns/op
|
||||
BenchmarkGval/single_parameter_evaluation-4 10000000 165 ns/op
|
||||
BenchmarkGval/single_parameter_parsing-4 1000000 1648 ns/op
|
||||
BenchmarkGval/parameter_evaluation-4 5000000 352 ns/op
|
||||
BenchmarkGval/parameter_parsing-4 500000 2773 ns/op
|
||||
BenchmarkGval/common_evaluation-4 3000000 434 ns/op
|
||||
BenchmarkGval/common_parsing-4 300000 4419 ns/op
|
||||
BenchmarkGval/complex_evaluation-4 100000000 11.6 ns/op
|
||||
BenchmarkGval/complex_parsing-4 100000 17936 ns/op
|
||||
BenchmarkGval/literal_evaluation-4 300000000 3.84 ns/op
|
||||
BenchmarkGval/literal_parsing-4 500000 2559 ns/op
|
||||
BenchmarkGval/modifier_evaluation-4 500000000 3.54 ns/op
|
||||
BenchmarkGval/modifier_parsing-4 500000 3755 ns/op
|
||||
BenchmarkGval/regex_evaluation-4 50000 21347 ns/op
|
||||
BenchmarkGval/regex_parsing-4 200000 6480 ns/op
|
||||
BenchmarkGval/constant_regex_evaluation-4 1000000 1000 ns/op
|
||||
BenchmarkGval/constant_regex_parsing-4 200000 9417 ns/op
|
||||
BenchmarkGval/accessors_evaluation-4 3000000 417 ns/op
|
||||
BenchmarkGval/accessors_parsing-4 1000000 1778 ns/op
|
||||
BenchmarkGval/accessors_method_evaluation-4 1000000 1931 ns/op
|
||||
BenchmarkGval/accessors_method_parsing-4 1000000 1729 ns/op
|
||||
BenchmarkGval/accessors_method_parameter_evaluation-4 1000000 2162 ns/op
|
||||
BenchmarkGval/accessors_method_parameter_parsing-4 500000 2618 ns/op
|
||||
BenchmarkGval/nested_accessors_evaluation-4 2000000 681 ns/op
|
||||
BenchmarkGval/nested_accessors_parsing-4 1000000 2115 ns/op
|
||||
BenchmarkRandom-4 500000 3631 ns/op
|
||||
ok
|
||||
```
|
||||
|
||||
## API Breaks
|
||||
|
||||
Gval is designed with easy expandability in mind and API breaks will be avoided if possible. If API breaks are unavoidable they wil be explicitly stated via an increased major version number.
|
||||
|
||||
-------------------------------------
|
||||
Credits to Reene French for the gophers.
|
||||
363
server/vendor/github.com/PaesslerAG/gval/evaluable.go
generated
vendored
Normal file
363
server/vendor/github.com/PaesslerAG/gval/evaluable.go
generated
vendored
Normal file
@@ -0,0 +1,363 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Selector allows for custom variable selection from structs
|
||||
//
|
||||
// Return value is again handled with variable() until end of the given path
|
||||
type Selector interface {
|
||||
SelectGVal(c context.Context, key string) (interface{}, error)
|
||||
}
|
||||
|
||||
// Evaluable evaluates given parameter
|
||||
type Evaluable func(c context.Context, parameter interface{}) (interface{}, error)
|
||||
|
||||
// EvalInt evaluates given parameter to an int
|
||||
func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error) {
|
||||
v, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
f, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
|
||||
}
|
||||
return int(f), nil
|
||||
}
|
||||
|
||||
// EvalFloat64 evaluates given parameter to a float64
|
||||
func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error) {
|
||||
v, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
f, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// EvalBool evaluates given parameter to a bool
|
||||
func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error) {
|
||||
v, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
b, ok := convertToBool(v)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("expected bool but got %v (%T)", v, v)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// EvalString evaluates given parameter to a string
|
||||
func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error) {
|
||||
o, err := e(c, parameter)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%v", o), nil
|
||||
}
|
||||
|
||||
// Const Evaluable represents given constant
|
||||
func (*Parser) Const(value interface{}) Evaluable {
|
||||
return constant(value)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func constant(value interface{}) Evaluable {
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Var Evaluable represents value at given path.
|
||||
// It supports with default language VariableSelector:
|
||||
//
|
||||
// map[interface{}]interface{},
|
||||
// map[string]interface{} and
|
||||
// []interface{} and via reflect
|
||||
// struct fields,
|
||||
// struct methods,
|
||||
// slices and
|
||||
// map with int or string key.
|
||||
func (p *Parser) Var(path ...Evaluable) Evaluable {
|
||||
if p.selector == nil {
|
||||
return variable(path)
|
||||
}
|
||||
return p.selector(path)
|
||||
}
|
||||
|
||||
// Evaluables is a slice of Evaluable.
|
||||
type Evaluables []Evaluable
|
||||
|
||||
// EvalStrings evaluates given parameter to a string slice
|
||||
func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error) {
|
||||
strs := make([]string, len(evs))
|
||||
for i, p := range evs {
|
||||
k, err := p.EvalString(c, parameter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
strs[i] = k
|
||||
}
|
||||
return strs, nil
|
||||
}
|
||||
|
||||
func variable(path Evaluables) Evaluable {
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
keys, err := path.EvalStrings(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, k := range keys {
|
||||
switch o := v.(type) {
|
||||
case Selector:
|
||||
v, err = o.SelectGVal(c, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select '%s' on %T: %w", k, o, err)
|
||||
}
|
||||
continue
|
||||
case map[interface{}]interface{}:
|
||||
v = o[k]
|
||||
continue
|
||||
case map[string]interface{}:
|
||||
v = o[k]
|
||||
continue
|
||||
case []interface{}:
|
||||
if i, err := strconv.Atoi(k); err == nil && i >= 0 && len(o) > i {
|
||||
v = o[i]
|
||||
continue
|
||||
}
|
||||
default:
|
||||
var ok bool
|
||||
v, ok = reflectSelect(k, o)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func reflectSelect(key string, value interface{}) (selection interface{}, ok bool) {
|
||||
vv := reflect.ValueOf(value)
|
||||
vvElem := resolvePotentialPointer(vv)
|
||||
|
||||
switch vvElem.Kind() {
|
||||
case reflect.Map:
|
||||
mapKey, ok := reflectConvertTo(vv.Type().Key().Kind(), key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
vvElem = vv.MapIndex(reflect.ValueOf(mapKey))
|
||||
vvElem = resolvePotentialPointer(vvElem)
|
||||
|
||||
if vvElem.IsValid() {
|
||||
return vvElem.Interface(), true
|
||||
}
|
||||
|
||||
// key didn't exist. Check if there is a bound method
|
||||
method := vv.MethodByName(key)
|
||||
if method.IsValid() {
|
||||
return method.Interface(), true
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
if i, err := strconv.Atoi(key); err == nil && i >= 0 && vv.Len() > i {
|
||||
vvElem = resolvePotentialPointer(vv.Index(i))
|
||||
return vvElem.Interface(), true
|
||||
}
|
||||
|
||||
// key not an int. Check if there is a bound method
|
||||
method := vv.MethodByName(key)
|
||||
if method.IsValid() {
|
||||
return method.Interface(), true
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
field := vvElem.FieldByName(key)
|
||||
if field.IsValid() {
|
||||
return field.Interface(), true
|
||||
}
|
||||
|
||||
method := vv.MethodByName(key)
|
||||
if method.IsValid() {
|
||||
return method.Interface(), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func resolvePotentialPointer(value reflect.Value) reflect.Value {
|
||||
if value.Kind() == reflect.Ptr {
|
||||
return value.Elem()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func reflectConvertTo(k reflect.Kind, value string) (interface{}, bool) {
|
||||
switch k {
|
||||
case reflect.String:
|
||||
return value, true
|
||||
case reflect.Int:
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (*Parser) callFunc(fun function, args ...Evaluable) Evaluable {
|
||||
return func(c context.Context, v interface{}) (ret interface{}, err error) {
|
||||
a := make([]interface{}, len(args))
|
||||
for i, arg := range args {
|
||||
ai, err := arg(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a[i] = ai
|
||||
}
|
||||
return fun(c, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func (*Parser) callEvaluable(fullname string, fun Evaluable, args ...Evaluable) Evaluable {
|
||||
return func(c context.Context, v interface{}) (ret interface{}, err error) {
|
||||
f, err := fun(c, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not call function: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("failed to execute function '%s': %s", fullname, r)
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
|
||||
ff := reflect.ValueOf(f)
|
||||
|
||||
if ff.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("could not call '%s' type %T", fullname, f)
|
||||
}
|
||||
|
||||
a := make([]reflect.Value, len(args))
|
||||
for i := range args {
|
||||
arg, err := args[i](c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a[i] = reflect.ValueOf(arg)
|
||||
}
|
||||
|
||||
rr := ff.Call(a)
|
||||
|
||||
r := make([]interface{}, len(rr))
|
||||
for i, e := range rr {
|
||||
r[i] = e.Interface()
|
||||
}
|
||||
|
||||
errorInterface := reflect.TypeOf((*error)(nil)).Elem()
|
||||
if len(r) > 0 && ff.Type().Out(len(r)-1).Implements(errorInterface) {
|
||||
if r[len(r)-1] != nil {
|
||||
err = r[len(r)-1].(error)
|
||||
}
|
||||
r = r[0 : len(r)-1]
|
||||
}
|
||||
|
||||
switch len(r) {
|
||||
case 0:
|
||||
return err, nil
|
||||
case 1:
|
||||
return r[0], err
|
||||
default:
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsConst returns if the Evaluable is a Parser.Const() value
|
||||
func (e Evaluable) IsConst() bool {
|
||||
pc := reflect.ValueOf(constant(nil)).Pointer()
|
||||
pe := reflect.ValueOf(e).Pointer()
|
||||
return pc == pe
|
||||
}
|
||||
|
||||
func regEx(a, b Evaluable) (Evaluable, error) {
|
||||
if !b.IsConst() {
|
||||
return func(c context.Context, o interface{}) (interface{}, error) {
|
||||
a, err := a.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := b.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matched, err := regexp.MatchString(b, a)
|
||||
return matched, err
|
||||
}, nil
|
||||
}
|
||||
s, err := b.EvalString(context.TODO(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
regex, err := regexp.Compile(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
s, err := a.EvalString(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return regex.MatchString(s), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func notRegEx(a, b Evaluable) (Evaluable, error) {
|
||||
if !b.IsConst() {
|
||||
return func(c context.Context, o interface{}) (interface{}, error) {
|
||||
a, err := a.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := b.EvalString(c, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matched, err := regexp.MatchString(b, a)
|
||||
return !matched, err
|
||||
}, nil
|
||||
}
|
||||
s, err := b.EvalString(context.TODO(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
regex, err := regexp.Compile(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
s, err := a.EvalString(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return !regex.MatchString(s), nil
|
||||
}, nil
|
||||
}
|
||||
128
server/vendor/github.com/PaesslerAG/gval/functions.go
generated
vendored
Normal file
128
server/vendor/github.com/PaesslerAG/gval/functions.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type function func(ctx context.Context, arguments ...interface{}) (interface{}, error)
|
||||
|
||||
func toFunc(f interface{}) function {
|
||||
if f, ok := f.(func(arguments ...interface{}) (interface{}, error)); ok {
|
||||
return function(func(ctx context.Context, arguments ...interface{}) (interface{}, error) {
|
||||
var v interface{}
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
errCh <- fmt.Errorf("%v", recovered)
|
||||
}
|
||||
}()
|
||||
result, err := f(arguments...)
|
||||
v = result
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case err := <-errCh:
|
||||
close(errCh)
|
||||
return v, err
|
||||
}
|
||||
})
|
||||
}
|
||||
if f, ok := f.(func(ctx context.Context, arguments ...interface{}) (interface{}, error)); ok {
|
||||
return function(f)
|
||||
}
|
||||
|
||||
fun := reflect.ValueOf(f)
|
||||
t := fun.Type()
|
||||
return func(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
var v interface{}
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
errCh <- fmt.Errorf("%v", recovered)
|
||||
}
|
||||
}()
|
||||
in, err := createCallArguments(ctx, t, args)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
out := fun.Call(in)
|
||||
|
||||
r := make([]interface{}, len(out))
|
||||
for i, e := range out {
|
||||
r[i] = e.Interface()
|
||||
}
|
||||
|
||||
err = nil
|
||||
errorInterface := reflect.TypeOf((*error)(nil)).Elem()
|
||||
if len(r) > 0 && t.Out(len(r)-1).Implements(errorInterface) {
|
||||
if r[len(r)-1] != nil {
|
||||
err = r[len(r)-1].(error)
|
||||
}
|
||||
r = r[0 : len(r)-1]
|
||||
}
|
||||
|
||||
switch len(r) {
|
||||
case 0:
|
||||
v = nil
|
||||
case 1:
|
||||
v = r[0]
|
||||
default:
|
||||
v = r
|
||||
}
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case err := <-errCh:
|
||||
close(errCh)
|
||||
return v, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createCallArguments(ctx context.Context, t reflect.Type, args []interface{}) ([]reflect.Value, error) {
|
||||
variadic := t.IsVariadic()
|
||||
numIn := t.NumIn()
|
||||
|
||||
// if first argument is a context, use the given execution context
|
||||
if numIn > 0 {
|
||||
thisFun := reflect.ValueOf(createCallArguments)
|
||||
thisT := thisFun.Type()
|
||||
if t.In(0) == thisT.In(0) {
|
||||
args = append([]interface{}{ctx}, args...)
|
||||
}
|
||||
}
|
||||
|
||||
if (!variadic && len(args) != numIn) || (variadic && len(args) < numIn-1) {
|
||||
return nil, fmt.Errorf("invalid number of parameters")
|
||||
}
|
||||
|
||||
in := make([]reflect.Value, len(args))
|
||||
var inType reflect.Type
|
||||
for i, arg := range args {
|
||||
if !variadic || i < numIn-1 {
|
||||
inType = t.In(i)
|
||||
} else if i == numIn-1 {
|
||||
inType = t.In(numIn - 1).Elem()
|
||||
}
|
||||
argVal := reflect.ValueOf(arg)
|
||||
if arg == nil {
|
||||
argVal = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem())
|
||||
} else if !argVal.Type().AssignableTo(inType) {
|
||||
return nil, fmt.Errorf("expected type %s for parameter %d but got %T",
|
||||
inType.String(), i, arg)
|
||||
}
|
||||
in[i] = argVal
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
336
server/vendor/github.com/PaesslerAG/gval/gval.go
generated
vendored
Normal file
336
server/vendor/github.com/PaesslerAG/gval/gval.go
generated
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
// Package gval provides a generic expression language.
|
||||
// All functions, infix and prefix operators can be replaced by composing languages into a new one.
|
||||
//
|
||||
// The package contains concrete expression languages for common application in text, arithmetic, decimal arithmetic, propositional logic and so on.
|
||||
// They can be used as basis for a custom expression language or to evaluate expressions directly.
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"text/scanner"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// Evaluate given parameter with given expression in gval full language
|
||||
func Evaluate(expression string, parameter interface{}, opts ...Language) (interface{}, error) {
|
||||
return EvaluateWithContext(context.Background(), expression, parameter, opts...)
|
||||
}
|
||||
|
||||
// Evaluate given parameter with given expression in gval full language using a context
|
||||
func EvaluateWithContext(c context.Context, expression string, parameter interface{}, opts ...Language) (interface{}, error) {
|
||||
l := full
|
||||
if len(opts) > 0 {
|
||||
l = NewLanguage(append([]Language{l}, opts...)...)
|
||||
}
|
||||
return l.EvaluateWithContext(c, expression, parameter)
|
||||
}
|
||||
|
||||
// Full is the union of Arithmetic, Bitmask, Text, PropositionalLogic, TernaryOperator, and Json
|
||||
//
|
||||
// Operator in: a in b is true iff value a is an element of array b
|
||||
// Operator ??: a ?? b returns a if a is not false or nil, otherwise n
|
||||
//
|
||||
// Function Date: Date(a) parses string a. a must match RFC3339, ISO8601, ruby date, or unix date
|
||||
func Full(extensions ...Language) Language {
|
||||
if len(extensions) == 0 {
|
||||
return full
|
||||
}
|
||||
return NewLanguage(append([]Language{full}, extensions...)...)
|
||||
}
|
||||
|
||||
// TernaryOperator contains following Operator
|
||||
//
|
||||
// ?: a ? b : c returns b if bool a is true, otherwise b
|
||||
func TernaryOperator() Language {
|
||||
return ternaryOperator
|
||||
}
|
||||
|
||||
// Arithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-)
|
||||
// and numerical order (<=,<,>,>=)
|
||||
//
|
||||
// Arithmetic operators expect float64 operands.
|
||||
// Called with unfitting input, they try to convert the input to float64.
|
||||
// They can parse strings and convert any type of int or float.
|
||||
func Arithmetic() Language {
|
||||
return arithmetic
|
||||
}
|
||||
|
||||
// DecimalArithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-)
|
||||
// and numerical order (<=,<,>,>=)
|
||||
//
|
||||
// DecimalArithmetic operators expect decimal.Decimal operands (github.com/shopspring/decimal)
|
||||
// and are used to calculate money/decimal rather than floating point calculations.
|
||||
// Called with unfitting input, they try to convert the input to decimal.Decimal.
|
||||
// They can parse strings and convert any type of int or float.
|
||||
func DecimalArithmetic() Language {
|
||||
return decimalArithmetic
|
||||
}
|
||||
|
||||
// Bitmask contains base, bitwise and(&), bitwise or(|) and bitwise not(^).
|
||||
//
|
||||
// Bitmask operators expect float64 operands.
|
||||
// Called with unfitting input they try to convert the input to float64.
|
||||
// They can parse strings and convert any type of int or float.
|
||||
func Bitmask() Language {
|
||||
return bitmask
|
||||
}
|
||||
|
||||
// Text contains base, lexical order on strings (<=,<,>,>=),
|
||||
// regex match (=~) and regex not match (!~)
|
||||
func Text() Language {
|
||||
return text
|
||||
}
|
||||
|
||||
// PropositionalLogic contains base, not(!), and (&&), or (||) and Base.
|
||||
//
|
||||
// Propositional operator expect bool operands.
|
||||
// Called with unfitting input they try to convert the input to bool.
|
||||
// Numbers other than 0 and the strings "TRUE" and "true" are interpreted as true.
|
||||
// 0 and the strings "FALSE" and "false" are interpreted as false.
|
||||
func PropositionalLogic() Language {
|
||||
return propositionalLogic
|
||||
}
|
||||
|
||||
// JSON contains json objects ({string:expression,...})
|
||||
// and json arrays ([expression, ...])
|
||||
func JSON() Language {
|
||||
return ljson
|
||||
}
|
||||
|
||||
// Parentheses contains support for parentheses.
|
||||
func Parentheses() Language {
|
||||
return parentheses
|
||||
}
|
||||
|
||||
// Ident contains support for variables and functions.
|
||||
func Ident() Language {
|
||||
return ident
|
||||
}
|
||||
|
||||
// Base contains equal (==) and not equal (!=), perentheses and general support for variables, constants and functions
|
||||
// It contains true, false, (floating point) number, string ("" or “) and char (”) constants
|
||||
func Base() Language {
|
||||
return base
|
||||
}
|
||||
|
||||
var full = NewLanguage(arithmetic, bitmask, text, propositionalLogic, ljson,
|
||||
|
||||
InfixOperator("in", inArray),
|
||||
|
||||
InfixShortCircuit("??", func(a interface{}) (interface{}, bool) {
|
||||
v := reflect.ValueOf(a)
|
||||
return a, a != nil && !v.IsZero()
|
||||
}),
|
||||
InfixOperator("??", func(a, b interface{}) (interface{}, error) {
|
||||
if v := reflect.ValueOf(a); a == nil || v.IsZero() {
|
||||
return b, nil
|
||||
}
|
||||
return a, nil
|
||||
}),
|
||||
|
||||
ternaryOperator,
|
||||
|
||||
Function("date", func(arguments ...interface{}) (interface{}, error) {
|
||||
if len(arguments) != 1 {
|
||||
return nil, fmt.Errorf("date() expects exactly one string argument")
|
||||
}
|
||||
s, ok := arguments[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("date() expects exactly one string argument")
|
||||
}
|
||||
for _, format := range [...]string{
|
||||
time.ANSIC,
|
||||
time.UnixDate,
|
||||
time.RubyDate,
|
||||
time.Kitchen,
|
||||
time.RFC3339,
|
||||
time.RFC3339Nano,
|
||||
"2006-01-02", // RFC 3339
|
||||
"2006-01-02 15:04", // RFC 3339 with minutes
|
||||
"2006-01-02 15:04:05", // RFC 3339 with seconds
|
||||
"2006-01-02 15:04:05-07:00", // RFC 3339 with seconds and timezone
|
||||
"2006-01-02T15Z0700", // ISO8601 with hour
|
||||
"2006-01-02T15:04Z0700", // ISO8601 with minutes
|
||||
"2006-01-02T15:04:05Z0700", // ISO8601 with seconds
|
||||
"2006-01-02T15:04:05.999999999Z0700", // ISO8601 with nanoseconds
|
||||
} {
|
||||
ret, err := time.ParseInLocation(format, s, time.Local)
|
||||
if err == nil {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("date() could not parse %s", s)
|
||||
}),
|
||||
)
|
||||
|
||||
var ternaryOperator = PostfixOperator("?", parseIf)
|
||||
|
||||
var ljson = NewLanguage(
|
||||
PrefixExtension('[', parseJSONArray),
|
||||
PrefixExtension('{', parseJSONObject),
|
||||
)
|
||||
|
||||
var arithmetic = NewLanguage(
|
||||
InfixNumberOperator("+", func(a, b float64) (interface{}, error) { return a + b, nil }),
|
||||
InfixNumberOperator("-", func(a, b float64) (interface{}, error) { return a - b, nil }),
|
||||
InfixNumberOperator("*", func(a, b float64) (interface{}, error) { return a * b, nil }),
|
||||
InfixNumberOperator("/", func(a, b float64) (interface{}, error) { return a / b, nil }),
|
||||
InfixNumberOperator("%", func(a, b float64) (interface{}, error) { return math.Mod(a, b), nil }),
|
||||
InfixNumberOperator("**", func(a, b float64) (interface{}, error) { return math.Pow(a, b), nil }),
|
||||
|
||||
InfixNumberOperator(">", func(a, b float64) (interface{}, error) { return a > b, nil }),
|
||||
InfixNumberOperator(">=", func(a, b float64) (interface{}, error) { return a >= b, nil }),
|
||||
InfixNumberOperator("<", func(a, b float64) (interface{}, error) { return a < b, nil }),
|
||||
InfixNumberOperator("<=", func(a, b float64) (interface{}, error) { return a <= b, nil }),
|
||||
|
||||
InfixNumberOperator("==", func(a, b float64) (interface{}, error) { return a == b, nil }),
|
||||
InfixNumberOperator("!=", func(a, b float64) (interface{}, error) { return a != b, nil }),
|
||||
|
||||
base,
|
||||
)
|
||||
|
||||
var decimalArithmetic = NewLanguage(
|
||||
InfixDecimalOperator("+", func(a, b decimal.Decimal) (interface{}, error) { return a.Add(b), nil }),
|
||||
InfixDecimalOperator("-", func(a, b decimal.Decimal) (interface{}, error) { return a.Sub(b), nil }),
|
||||
InfixDecimalOperator("*", func(a, b decimal.Decimal) (interface{}, error) { return a.Mul(b), nil }),
|
||||
InfixDecimalOperator("/", func(a, b decimal.Decimal) (interface{}, error) { return a.Div(b), nil }),
|
||||
InfixDecimalOperator("%", func(a, b decimal.Decimal) (interface{}, error) { return a.Mod(b), nil }),
|
||||
InfixDecimalOperator("**", func(a, b decimal.Decimal) (interface{}, error) { return a.Pow(b), nil }),
|
||||
|
||||
InfixDecimalOperator(">", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThan(b), nil }),
|
||||
InfixDecimalOperator(">=", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThanOrEqual(b), nil }),
|
||||
InfixDecimalOperator("<", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThan(b), nil }),
|
||||
InfixDecimalOperator("<=", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThanOrEqual(b), nil }),
|
||||
|
||||
InfixDecimalOperator("==", func(a, b decimal.Decimal) (interface{}, error) { return a.Equal(b), nil }),
|
||||
InfixDecimalOperator("!=", func(a, b decimal.Decimal) (interface{}, error) { return !a.Equal(b), nil }),
|
||||
base,
|
||||
//Base is before these overrides so that the Base options are overridden
|
||||
PrefixExtension(scanner.Int, parseDecimal),
|
||||
PrefixExtension(scanner.Float, parseDecimal),
|
||||
PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
i, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
|
||||
}
|
||||
return decimal.NewFromFloat(i).Neg(), nil
|
||||
}),
|
||||
)
|
||||
|
||||
var bitmask = NewLanguage(
|
||||
InfixNumberOperator("^", func(a, b float64) (interface{}, error) { return float64(int64(a) ^ int64(b)), nil }),
|
||||
InfixNumberOperator("&", func(a, b float64) (interface{}, error) { return float64(int64(a) & int64(b)), nil }),
|
||||
InfixNumberOperator("|", func(a, b float64) (interface{}, error) { return float64(int64(a) | int64(b)), nil }),
|
||||
InfixNumberOperator("<<", func(a, b float64) (interface{}, error) { return float64(int64(a) << uint64(b)), nil }),
|
||||
InfixNumberOperator(">>", func(a, b float64) (interface{}, error) { return float64(int64(a) >> uint64(b)), nil }),
|
||||
|
||||
PrefixOperator("~", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
i, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %T expected number", v)
|
||||
}
|
||||
return float64(^int64(i)), nil
|
||||
}),
|
||||
)
|
||||
|
||||
var text = NewLanguage(
|
||||
InfixTextOperator("+", func(a, b string) (interface{}, error) { return fmt.Sprintf("%v%v", a, b), nil }),
|
||||
|
||||
InfixTextOperator("<", func(a, b string) (interface{}, error) { return a < b, nil }),
|
||||
InfixTextOperator("<=", func(a, b string) (interface{}, error) { return a <= b, nil }),
|
||||
InfixTextOperator(">", func(a, b string) (interface{}, error) { return a > b, nil }),
|
||||
InfixTextOperator(">=", func(a, b string) (interface{}, error) { return a >= b, nil }),
|
||||
|
||||
InfixEvalOperator("=~", regEx),
|
||||
InfixEvalOperator("!~", notRegEx),
|
||||
base,
|
||||
)
|
||||
|
||||
var propositionalLogic = NewLanguage(
|
||||
PrefixOperator("!", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
b, ok := convertToBool(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %T expected bool", v)
|
||||
}
|
||||
return !b, nil
|
||||
}),
|
||||
|
||||
InfixShortCircuit("&&", func(a interface{}) (interface{}, bool) { return false, a == false }),
|
||||
InfixBoolOperator("&&", func(a, b bool) (interface{}, error) { return a && b, nil }),
|
||||
InfixShortCircuit("||", func(a interface{}) (interface{}, bool) { return true, a == true }),
|
||||
InfixBoolOperator("||", func(a, b bool) (interface{}, error) { return a || b, nil }),
|
||||
|
||||
InfixBoolOperator("==", func(a, b bool) (interface{}, error) { return a == b, nil }),
|
||||
InfixBoolOperator("!=", func(a, b bool) (interface{}, error) { return a != b, nil }),
|
||||
|
||||
base,
|
||||
)
|
||||
|
||||
var parentheses = NewLanguage(
|
||||
PrefixExtension('(', parseParentheses),
|
||||
)
|
||||
|
||||
var ident = NewLanguage(
|
||||
PrefixMetaPrefix(scanner.Ident, parseIdent),
|
||||
)
|
||||
|
||||
var base = NewLanguage(
|
||||
PrefixExtension(scanner.Int, parseNumber),
|
||||
PrefixExtension(scanner.Float, parseNumber),
|
||||
PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
|
||||
i, ok := convertToFloat(v)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
|
||||
}
|
||||
return -i, nil
|
||||
}),
|
||||
|
||||
PrefixExtension(scanner.String, parseString),
|
||||
PrefixExtension(scanner.Char, parseString),
|
||||
PrefixExtension(scanner.RawString, parseString),
|
||||
|
||||
Constant("true", true),
|
||||
Constant("false", false),
|
||||
|
||||
InfixOperator("==", func(a, b interface{}) (interface{}, error) { return reflect.DeepEqual(a, b), nil }),
|
||||
InfixOperator("!=", func(a, b interface{}) (interface{}, error) { return !reflect.DeepEqual(a, b), nil }),
|
||||
parentheses,
|
||||
|
||||
Precedence("??", 0),
|
||||
|
||||
Precedence("||", 20),
|
||||
Precedence("&&", 21),
|
||||
|
||||
Precedence("==", 40),
|
||||
Precedence("!=", 40),
|
||||
Precedence(">", 40),
|
||||
Precedence(">=", 40),
|
||||
Precedence("<", 40),
|
||||
Precedence("<=", 40),
|
||||
Precedence("=~", 40),
|
||||
Precedence("!~", 40),
|
||||
Precedence("in", 40),
|
||||
|
||||
Precedence("^", 60),
|
||||
Precedence("&", 60),
|
||||
Precedence("|", 60),
|
||||
|
||||
Precedence("<<", 90),
|
||||
Precedence(">>", 90),
|
||||
|
||||
Precedence("+", 120),
|
||||
Precedence("-", 120),
|
||||
|
||||
Precedence("*", 150),
|
||||
Precedence("/", 150),
|
||||
Precedence("%", 150),
|
||||
|
||||
Precedence("**", 200),
|
||||
|
||||
ident,
|
||||
)
|
||||
281
server/vendor/github.com/PaesslerAG/gval/language.go
generated
vendored
Normal file
281
server/vendor/github.com/PaesslerAG/gval/language.go
generated
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// Language is an expression language
|
||||
type Language struct {
|
||||
prefixes map[interface{}]extension
|
||||
operators map[string]operator
|
||||
operatorSymbols map[rune]struct{}
|
||||
init extension
|
||||
def extension
|
||||
selector func(Evaluables) Evaluable
|
||||
}
|
||||
|
||||
// NewLanguage returns the union of given Languages as new Language.
|
||||
func NewLanguage(bases ...Language) Language {
|
||||
l := newLanguage()
|
||||
for _, base := range bases {
|
||||
for i, e := range base.prefixes {
|
||||
l.prefixes[i] = e
|
||||
}
|
||||
for i, e := range base.operators {
|
||||
l.operators[i] = e.merge(l.operators[i])
|
||||
l.operators[i].initiate(i)
|
||||
}
|
||||
for i := range base.operatorSymbols {
|
||||
l.operatorSymbols[i] = struct{}{}
|
||||
}
|
||||
if base.init != nil {
|
||||
l.init = base.init
|
||||
}
|
||||
if base.def != nil {
|
||||
l.def = base.def
|
||||
}
|
||||
if base.selector != nil {
|
||||
l.selector = base.selector
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func newLanguage() Language {
|
||||
return Language{
|
||||
prefixes: map[interface{}]extension{},
|
||||
operators: map[string]operator{},
|
||||
operatorSymbols: map[rune]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewEvaluable returns an Evaluable for given expression in the specified language
|
||||
func (l Language) NewEvaluable(expression string) (Evaluable, error) {
|
||||
return l.NewEvaluableWithContext(context.Background(), expression)
|
||||
}
|
||||
|
||||
// NewEvaluableWithContext returns an Evaluable for given expression in the specified language using context
|
||||
func (l Language) NewEvaluableWithContext(c context.Context, expression string) (Evaluable, error) {
|
||||
p := newParser(expression, l)
|
||||
|
||||
eval, err := p.parse(c)
|
||||
if err == nil && p.isCamouflaged() && p.lastScan != scanner.EOF {
|
||||
err = p.camouflage
|
||||
}
|
||||
if err != nil {
|
||||
pos := p.scanner.Pos()
|
||||
return nil, fmt.Errorf("parsing error: %s - %d:%d %w", p.scanner.Position, pos.Line, pos.Column, err)
|
||||
}
|
||||
|
||||
return eval, nil
|
||||
}
|
||||
|
||||
// Evaluate given parameter with given expression
|
||||
func (l Language) Evaluate(expression string, parameter interface{}) (interface{}, error) {
|
||||
return l.EvaluateWithContext(context.Background(), expression, parameter)
|
||||
}
|
||||
|
||||
// Evaluate given parameter with given expression using context
|
||||
func (l Language) EvaluateWithContext(c context.Context, expression string, parameter interface{}) (interface{}, error) {
|
||||
eval, err := l.NewEvaluableWithContext(c, expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := eval(c, parameter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not evaluate %s: %w", expression, err)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Function returns a Language with given function.
|
||||
// Function has no conversion for input types.
|
||||
//
|
||||
// If the function returns an error it must be the last return parameter.
|
||||
//
|
||||
// If the function has (without the error) more then one return parameter,
|
||||
// it returns them as []interface{}.
|
||||
func Function(name string, function interface{}) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[name] = func(c context.Context, p *Parser) (eval Evaluable, err error) {
|
||||
args := []Evaluable{}
|
||||
scan := p.Scan()
|
||||
switch scan {
|
||||
case '(':
|
||||
args, err = p.parseArguments(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
p.Camouflage("function call", '(')
|
||||
}
|
||||
return p.callFunc(toFunc(function), args...), nil
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Constant returns a Language with given constant
|
||||
func Constant(name string, value interface{}) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[l.makePrefixKey(name)] = func(c context.Context, p *Parser) (eval Evaluable, err error) {
|
||||
return p.Const(value), nil
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// PrefixExtension extends a Language
|
||||
func PrefixExtension(r rune, ext func(context.Context, *Parser) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[r] = ext
|
||||
return l
|
||||
}
|
||||
|
||||
// Init is a language that does no parsing, but invokes the given function when
|
||||
// parsing starts. It is incumbent upon the function to call ParseExpression to
|
||||
// continue parsing.
|
||||
//
|
||||
// This function can be used to customize the parser settings, such as
|
||||
// whitespace or ident behavior.
|
||||
func Init(ext func(context.Context, *Parser) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.init = ext
|
||||
return l
|
||||
}
|
||||
|
||||
// DefaultExtension is a language that runs the given function if no other
|
||||
// prefix matches.
|
||||
func DefaultExtension(ext func(context.Context, *Parser) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.def = ext
|
||||
return l
|
||||
}
|
||||
|
||||
// PrefixMetaPrefix chooses a Prefix to be executed
|
||||
func PrefixMetaPrefix(r rune, ext func(context.Context, *Parser) (call string, alternative func() (Evaluable, error), err error)) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[r] = func(c context.Context, p *Parser) (Evaluable, error) {
|
||||
call, alternative, err := ext(c, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if prefix, ok := p.prefixes[l.makePrefixKey(call)]; ok {
|
||||
return prefix(c, p)
|
||||
}
|
||||
return alternative()
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// PrefixOperator returns a Language with given prefix
|
||||
func PrefixOperator(name string, e Evaluable) Language {
|
||||
l := newLanguage()
|
||||
l.prefixes[l.makePrefixKey(name)] = func(c context.Context, p *Parser) (Evaluable, error) {
|
||||
eval, err := p.ParseNextExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefix := func(c context.Context, v interface{}) (interface{}, error) {
|
||||
a, err := eval(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e(c, a)
|
||||
}
|
||||
if eval.IsConst() {
|
||||
v, err := prefix(c, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefix = p.Const(v)
|
||||
}
|
||||
return prefix, nil
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// PostfixOperator extends a Language.
|
||||
func PostfixOperator(name string, ext func(context.Context, *Parser, Evaluable) (Evaluable, error)) Language {
|
||||
l := newLanguage()
|
||||
l.operators[l.makeInfixKey(name)] = postfix{
|
||||
f: func(c context.Context, p *Parser, eval Evaluable, pre operatorPrecedence) (Evaluable, error) {
|
||||
return ext(c, p, eval)
|
||||
},
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// InfixOperator for two arbitrary values.
|
||||
func InfixOperator(name string, f func(a, b interface{}) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{arbitrary: f})
|
||||
}
|
||||
|
||||
// InfixShortCircuit operator is called after the left operand is evaluated.
|
||||
func InfixShortCircuit(name string, f func(a interface{}) (interface{}, bool)) Language {
|
||||
return newLanguageOperator(name, &infix{shortCircuit: f})
|
||||
}
|
||||
|
||||
// InfixTextOperator for two text values.
|
||||
func InfixTextOperator(name string, f func(a, b string) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{text: f})
|
||||
}
|
||||
|
||||
// InfixNumberOperator for two number values.
|
||||
func InfixNumberOperator(name string, f func(a, b float64) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{number: f})
|
||||
}
|
||||
|
||||
// InfixDecimalOperator for two decimal values.
|
||||
func InfixDecimalOperator(name string, f func(a, b decimal.Decimal) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{decimal: f})
|
||||
}
|
||||
|
||||
// InfixBoolOperator for two bool values.
|
||||
func InfixBoolOperator(name string, f func(a, b bool) (interface{}, error)) Language {
|
||||
return newLanguageOperator(name, &infix{boolean: f})
|
||||
}
|
||||
|
||||
// Precedence of operator. The Operator with higher operatorPrecedence is evaluated first.
|
||||
func Precedence(name string, operatorPrecendence uint8) Language {
|
||||
return newLanguageOperator(name, operatorPrecedence(operatorPrecendence))
|
||||
}
|
||||
|
||||
// InfixEvalOperator operates on the raw operands.
|
||||
// Therefore it cannot be combined with operators for other operand types.
|
||||
func InfixEvalOperator(name string, f func(a, b Evaluable) (Evaluable, error)) Language {
|
||||
return newLanguageOperator(name, directInfix{infixBuilder: f})
|
||||
}
|
||||
|
||||
func newLanguageOperator(name string, op operator) Language {
|
||||
op.initiate(name)
|
||||
l := newLanguage()
|
||||
l.operators[l.makeInfixKey(name)] = op
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Language) makePrefixKey(key string) interface{} {
|
||||
runes := []rune(key)
|
||||
if len(runes) == 1 && !unicode.IsLetter(runes[0]) {
|
||||
return runes[0]
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func (l *Language) makeInfixKey(key string) string {
|
||||
for _, r := range key {
|
||||
l.operatorSymbols[r] = struct{}{}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// VariableSelector returns a Language which uses given variable selector.
|
||||
// It must be combined with a Language that uses the vatiable selector. E.g. gval.Base().
|
||||
func VariableSelector(selector func(path Evaluables) Evaluable) Language {
|
||||
l := newLanguage()
|
||||
l.selector = selector
|
||||
return l
|
||||
}
|
||||
405
server/vendor/github.com/PaesslerAG/gval/operator.go
generated
vendored
Normal file
405
server/vendor/github.com/PaesslerAG/gval/operator.go
generated
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type stage struct {
|
||||
Evaluable
|
||||
infixBuilder
|
||||
operatorPrecedence
|
||||
}
|
||||
|
||||
type stageStack []stage //operatorPrecedence in stacktStage is continuously, monotone ascending
|
||||
|
||||
func (s *stageStack) push(b stage) error {
|
||||
for len(*s) > 0 && s.peek().operatorPrecedence >= b.operatorPrecedence {
|
||||
a := s.pop()
|
||||
eval, err := a.infixBuilder(a.Evaluable, b.Evaluable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.IsConst() && b.IsConst() {
|
||||
v, err := eval(nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Evaluable = constant(v)
|
||||
continue
|
||||
}
|
||||
b.Evaluable = eval
|
||||
}
|
||||
*s = append(*s, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stageStack) peek() stage {
|
||||
return (*s)[len(*s)-1]
|
||||
}
|
||||
|
||||
func (s *stageStack) pop() stage {
|
||||
a := s.peek()
|
||||
(*s) = (*s)[:len(*s)-1]
|
||||
return a
|
||||
}
|
||||
|
||||
type infixBuilder func(a, b Evaluable) (Evaluable, error)
|
||||
|
||||
func (l Language) isSymbolOperation(r rune) bool {
|
||||
_, in := l.operatorSymbols[r]
|
||||
return in
|
||||
}
|
||||
|
||||
func (l Language) isOperatorPrefix(op string) bool {
|
||||
for k := range l.operators {
|
||||
if strings.HasPrefix(k, op) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (op *infix) initiate(name string) {
|
||||
f := func(a, b interface{}) (interface{}, error) {
|
||||
return nil, fmt.Errorf("invalid operation (%T) %s (%T)", a, name, b)
|
||||
}
|
||||
if op.arbitrary != nil {
|
||||
f = op.arbitrary
|
||||
}
|
||||
for _, typeConvertion := range []bool{true, false} {
|
||||
if op.text != nil && (!typeConvertion || op.arbitrary == nil) {
|
||||
f = getStringOpFunc(op.text, f, typeConvertion)
|
||||
}
|
||||
if op.boolean != nil {
|
||||
f = getBoolOpFunc(op.boolean, f, typeConvertion)
|
||||
}
|
||||
if op.number != nil {
|
||||
f = getFloatOpFunc(op.number, f, typeConvertion)
|
||||
}
|
||||
if op.decimal != nil {
|
||||
f = getDecimalOpFunc(op.decimal, f, typeConvertion)
|
||||
}
|
||||
}
|
||||
if op.shortCircuit == nil {
|
||||
op.builder = func(a, b Evaluable) (Evaluable, error) {
|
||||
return func(c context.Context, x interface{}) (interface{}, error) {
|
||||
a, err := a(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := b(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f(a, b)
|
||||
}, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
shortF := op.shortCircuit
|
||||
op.builder = func(a, b Evaluable) (Evaluable, error) {
|
||||
return func(c context.Context, x interface{}) (interface{}, error) {
|
||||
a, err := a(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r, ok := shortF(a); ok {
|
||||
return r, nil
|
||||
}
|
||||
b, err := b(c, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f(a, b)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type opFunc func(a, b interface{}) (interface{}, error)
|
||||
|
||||
func getStringOpFunc(s func(a, b string) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
if a != nil && b != nil {
|
||||
return s(fmt.Sprintf("%v", a), fmt.Sprintf("%v", b))
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
s1, k := a.(string)
|
||||
s2, l := b.(string)
|
||||
if k && l {
|
||||
return s(s1, s2)
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
func convertToBool(o interface{}) (bool, bool) {
|
||||
if b, ok := o.(bool); ok {
|
||||
return b, true
|
||||
}
|
||||
v := reflect.ValueOf(o)
|
||||
|
||||
if v.Kind() == reflect.Func {
|
||||
if vt := v.Type(); vt.NumIn() == 0 && vt.NumOut() == 1 {
|
||||
retType := vt.Out(0)
|
||||
|
||||
if retType.Kind() == reflect.Bool {
|
||||
funcResults := v.Call([]reflect.Value{})
|
||||
v = funcResults[0]
|
||||
o = v.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for o != nil && v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
if !v.IsValid() {
|
||||
return false, false
|
||||
}
|
||||
o = v.Interface()
|
||||
}
|
||||
|
||||
if o == false || o == nil || o == "false" || o == "FALSE" {
|
||||
return false, true
|
||||
}
|
||||
if o == true || o == "true" || o == "TRUE" {
|
||||
return true, true
|
||||
}
|
||||
if f, ok := convertToFloat(o); ok {
|
||||
return f != 0., true
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
func getBoolOpFunc(o func(a, b bool) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := convertToBool(a)
|
||||
y, l := convertToBool(b)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := a.(bool)
|
||||
y, l := b.(bool)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
func convertToFloat(o interface{}) (float64, bool) {
|
||||
if i, ok := o.(float64); ok {
|
||||
return i, true
|
||||
}
|
||||
v := reflect.ValueOf(o)
|
||||
for o != nil && v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
if !v.IsValid() {
|
||||
return 0, false
|
||||
}
|
||||
o = v.Interface()
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(v.Int()), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float64(v.Uint()), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float(), true
|
||||
}
|
||||
if s, ok := o.(string); ok {
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err == nil {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
func getFloatOpFunc(o func(a, b float64) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := convertToFloat(a)
|
||||
y, l := convertToFloat(b)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := a.(float64)
|
||||
y, l := b.(float64)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
func convertToDecimal(o interface{}) (decimal.Decimal, bool) {
|
||||
if i, ok := o.(decimal.Decimal); ok {
|
||||
return i, true
|
||||
}
|
||||
if i, ok := o.(float64); ok {
|
||||
return decimal.NewFromFloat(i), true
|
||||
}
|
||||
v := reflect.ValueOf(o)
|
||||
for o != nil && v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
if !v.IsValid() {
|
||||
return decimal.Zero, false
|
||||
}
|
||||
o = v.Interface()
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return decimal.NewFromInt(v.Int()), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return decimal.NewFromFloat(float64(v.Uint())), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return decimal.NewFromFloat(v.Float()), true
|
||||
}
|
||||
if s, ok := o.(string); ok {
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err == nil {
|
||||
return decimal.NewFromFloat(f), true
|
||||
}
|
||||
}
|
||||
return decimal.Zero, false
|
||||
}
|
||||
func getDecimalOpFunc(o func(a, b decimal.Decimal) (interface{}, error), f opFunc, typeConversion bool) opFunc {
|
||||
if typeConversion {
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := convertToDecimal(a)
|
||||
y, l := convertToDecimal(b)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
return func(a, b interface{}) (interface{}, error) {
|
||||
x, k := a.(decimal.Decimal)
|
||||
y, l := b.(decimal.Decimal)
|
||||
if k && l {
|
||||
return o(x, y)
|
||||
}
|
||||
|
||||
return f(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
type operator interface {
|
||||
merge(operator) operator
|
||||
precedence() operatorPrecedence
|
||||
initiate(name string)
|
||||
}
|
||||
|
||||
type operatorPrecedence uint8
|
||||
|
||||
func (pre operatorPrecedence) merge(op operator) operator {
|
||||
if op, ok := op.(operatorPrecedence); ok {
|
||||
if op > pre {
|
||||
return op
|
||||
}
|
||||
return pre
|
||||
}
|
||||
if op == nil {
|
||||
return pre
|
||||
}
|
||||
return op.merge(pre)
|
||||
}
|
||||
|
||||
func (pre operatorPrecedence) precedence() operatorPrecedence {
|
||||
return pre
|
||||
}
|
||||
|
||||
func (pre operatorPrecedence) initiate(name string) {}
|
||||
|
||||
type infix struct {
|
||||
operatorPrecedence
|
||||
number func(a, b float64) (interface{}, error)
|
||||
decimal func(a, b decimal.Decimal) (interface{}, error)
|
||||
boolean func(a, b bool) (interface{}, error)
|
||||
text func(a, b string) (interface{}, error)
|
||||
arbitrary func(a, b interface{}) (interface{}, error)
|
||||
shortCircuit func(a interface{}) (interface{}, bool)
|
||||
builder infixBuilder
|
||||
}
|
||||
|
||||
func (op infix) merge(op2 operator) operator {
|
||||
switch op2 := op2.(type) {
|
||||
case *infix:
|
||||
if op.number == nil {
|
||||
op.number = op2.number
|
||||
}
|
||||
if op.decimal == nil {
|
||||
op.decimal = op2.decimal
|
||||
}
|
||||
if op.boolean == nil {
|
||||
op.boolean = op2.boolean
|
||||
}
|
||||
if op.text == nil {
|
||||
op.text = op2.text
|
||||
}
|
||||
if op.arbitrary == nil {
|
||||
op.arbitrary = op2.arbitrary
|
||||
}
|
||||
if op.shortCircuit == nil {
|
||||
op.shortCircuit = op2.shortCircuit
|
||||
}
|
||||
}
|
||||
if op2 != nil && op2.precedence() > op.operatorPrecedence {
|
||||
op.operatorPrecedence = op2.precedence()
|
||||
}
|
||||
return &op
|
||||
}
|
||||
|
||||
type directInfix struct {
|
||||
operatorPrecedence
|
||||
infixBuilder
|
||||
}
|
||||
|
||||
func (op directInfix) merge(op2 operator) operator {
|
||||
switch op2 := op2.(type) {
|
||||
case operatorPrecedence:
|
||||
op.operatorPrecedence = op2
|
||||
}
|
||||
if op2 != nil && op2.precedence() > op.operatorPrecedence {
|
||||
op.operatorPrecedence = op2.precedence()
|
||||
}
|
||||
return op
|
||||
}
|
||||
|
||||
type extension func(context.Context, *Parser) (Evaluable, error)
|
||||
|
||||
type postfix struct {
|
||||
operatorPrecedence
|
||||
f func(context.Context, *Parser, Evaluable, operatorPrecedence) (Evaluable, error)
|
||||
}
|
||||
|
||||
func (op postfix) merge(op2 operator) operator {
|
||||
switch op2 := op2.(type) {
|
||||
case postfix:
|
||||
if op2.f != nil {
|
||||
op.f = op2.f
|
||||
}
|
||||
}
|
||||
if op2 != nil && op2.precedence() > op.operatorPrecedence {
|
||||
op.operatorPrecedence = op2.precedence()
|
||||
}
|
||||
return op
|
||||
}
|
||||
347
server/vendor/github.com/PaesslerAG/gval/parse.go
generated
vendored
Normal file
347
server/vendor/github.com/PaesslerAG/gval/parse.go
generated
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"text/scanner"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// ParseExpression scans an expression into an Evaluable.
|
||||
func (p *Parser) ParseExpression(c context.Context) (eval Evaluable, err error) {
|
||||
stack := stageStack{}
|
||||
for {
|
||||
eval, err = p.ParseNextExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stage, err := p.parseOperator(c, &stack, eval); err != nil {
|
||||
return nil, err
|
||||
} else if err = stack.push(stage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stack.peek().infixBuilder == nil {
|
||||
return stack.pop().Evaluable, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ParseNextExpression scans the expression ignoring following operators
|
||||
func (p *Parser) ParseNextExpression(c context.Context) (eval Evaluable, err error) {
|
||||
scan := p.Scan()
|
||||
ex, ok := p.prefixes[scan]
|
||||
if !ok {
|
||||
if scan != scanner.EOF && p.def != nil {
|
||||
return p.def(c, p)
|
||||
}
|
||||
return nil, p.Expected("extensions")
|
||||
}
|
||||
return ex(c, p)
|
||||
}
|
||||
|
||||
// ParseSublanguage sets the next language for this parser to parse and calls
|
||||
// its initialization function, usually ParseExpression.
|
||||
func (p *Parser) ParseSublanguage(c context.Context, l Language) (Evaluable, error) {
|
||||
if p.isCamouflaged() {
|
||||
panic("can not ParseSublanguage() on camouflaged Parser")
|
||||
}
|
||||
curLang := p.Language
|
||||
curWhitespace := p.scanner.Whitespace
|
||||
curMode := p.scanner.Mode
|
||||
curIsIdentRune := p.scanner.IsIdentRune
|
||||
|
||||
p.Language = l
|
||||
p.resetScannerProperties()
|
||||
|
||||
defer func() {
|
||||
p.Language = curLang
|
||||
p.scanner.Whitespace = curWhitespace
|
||||
p.scanner.Mode = curMode
|
||||
p.scanner.IsIdentRune = curIsIdentRune
|
||||
}()
|
||||
|
||||
return p.parse(c)
|
||||
}
|
||||
|
||||
func (p *Parser) parse(c context.Context) (Evaluable, error) {
|
||||
if p.init != nil {
|
||||
return p.init(c, p)
|
||||
}
|
||||
|
||||
return p.ParseExpression(c)
|
||||
}
|
||||
|
||||
func parseString(c context.Context, p *Parser) (Evaluable, error) {
|
||||
s, err := strconv.Unquote(p.TokenText())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse string: %w", err)
|
||||
}
|
||||
return p.Const(s), nil
|
||||
}
|
||||
|
||||
func parseNumber(c context.Context, p *Parser) (Evaluable, error) {
|
||||
n, err := strconv.ParseFloat(p.TokenText(), 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Const(n), nil
|
||||
}
|
||||
|
||||
func parseDecimal(c context.Context, p *Parser) (Evaluable, error) {
|
||||
n, err := strconv.ParseFloat(p.TokenText(), 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Const(decimal.NewFromFloat(n)), nil
|
||||
}
|
||||
|
||||
func parseParentheses(c context.Context, p *Parser) (Evaluable, error) {
|
||||
eval, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p.Scan() {
|
||||
case ')':
|
||||
return eval, nil
|
||||
default:
|
||||
return nil, p.Expected("parentheses", ')')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseOperator(c context.Context, stack *stageStack, eval Evaluable) (st stage, err error) {
|
||||
for {
|
||||
scan := p.Scan()
|
||||
op := p.TokenText()
|
||||
mustOp := false
|
||||
if p.isSymbolOperation(scan) {
|
||||
scan = p.Peek()
|
||||
for p.isSymbolOperation(scan) && p.isOperatorPrefix(op+string(scan)) {
|
||||
mustOp = true
|
||||
op += string(scan)
|
||||
p.Next()
|
||||
scan = p.Peek()
|
||||
}
|
||||
} else if scan != scanner.Ident {
|
||||
p.Camouflage("operator")
|
||||
return stage{Evaluable: eval}, nil
|
||||
}
|
||||
switch operator := p.operators[op].(type) {
|
||||
case *infix:
|
||||
return stage{
|
||||
Evaluable: eval,
|
||||
infixBuilder: operator.builder,
|
||||
operatorPrecedence: operator.operatorPrecedence,
|
||||
}, nil
|
||||
case directInfix:
|
||||
return stage{
|
||||
Evaluable: eval,
|
||||
infixBuilder: operator.infixBuilder,
|
||||
operatorPrecedence: operator.operatorPrecedence,
|
||||
}, nil
|
||||
case postfix:
|
||||
if err = stack.push(stage{
|
||||
operatorPrecedence: operator.operatorPrecedence,
|
||||
Evaluable: eval,
|
||||
}); err != nil {
|
||||
return stage{}, err
|
||||
}
|
||||
eval, err = operator.f(c, p, stack.pop().Evaluable, operator.operatorPrecedence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !mustOp {
|
||||
p.Camouflage("operator")
|
||||
return stage{Evaluable: eval}, nil
|
||||
}
|
||||
return stage{}, fmt.Errorf("unknown operator %s", op)
|
||||
}
|
||||
}
|
||||
|
||||
func parseIdent(c context.Context, p *Parser) (call string, alternative func() (Evaluable, error), err error) {
|
||||
token := p.TokenText()
|
||||
return token,
|
||||
func() (Evaluable, error) {
|
||||
fullname := token
|
||||
|
||||
keys := []Evaluable{p.Const(token)}
|
||||
for {
|
||||
scan := p.Scan()
|
||||
switch scan {
|
||||
case '.':
|
||||
scan = p.Scan()
|
||||
switch scan {
|
||||
case scanner.Ident:
|
||||
token = p.TokenText()
|
||||
keys = append(keys, p.Const(token))
|
||||
default:
|
||||
return nil, p.Expected("field", scanner.Ident)
|
||||
}
|
||||
case '(':
|
||||
args, err := p.parseArguments(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.callEvaluable(fullname, p.Var(keys...), args...), nil
|
||||
case '[':
|
||||
key, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p.Scan() {
|
||||
case ']':
|
||||
keys = append(keys, key)
|
||||
default:
|
||||
return nil, p.Expected("array key", ']')
|
||||
}
|
||||
default:
|
||||
p.Camouflage("variable", '.', '(', '[')
|
||||
return p.Var(keys...), nil
|
||||
}
|
||||
}
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (p *Parser) parseArguments(c context.Context) (args []Evaluable, err error) {
|
||||
if p.Scan() == ')' {
|
||||
return
|
||||
}
|
||||
p.Camouflage("scan arguments", ')')
|
||||
for {
|
||||
arg, err := p.ParseExpression(c)
|
||||
args = append(args, arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch p.Scan() {
|
||||
case ')':
|
||||
return args, nil
|
||||
case ',':
|
||||
default:
|
||||
return nil, p.Expected("arguments", ')', ',')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inArray(a, b interface{}) (interface{}, error) {
|
||||
col, ok := b.([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected type []interface{} for in operator but got %T", b)
|
||||
}
|
||||
for _, value := range col {
|
||||
if reflect.DeepEqual(a, value) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func parseIf(c context.Context, p *Parser, e Evaluable) (Evaluable, error) {
|
||||
a, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := p.Const(nil)
|
||||
switch p.Scan() {
|
||||
case ':':
|
||||
b, err = p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case scanner.EOF:
|
||||
default:
|
||||
return nil, p.Expected("<> ? <> : <>", ':', scanner.EOF)
|
||||
}
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
x, err := e(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if valX := reflect.ValueOf(x); x == nil || valX.IsZero() {
|
||||
return b(c, v)
|
||||
}
|
||||
return a(c, v)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseJSONArray(c context.Context, p *Parser) (Evaluable, error) {
|
||||
evals := []Evaluable{}
|
||||
for {
|
||||
switch p.Scan() {
|
||||
default:
|
||||
p.Camouflage("array", ',', ']')
|
||||
eval, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evals = append(evals, eval)
|
||||
case ',':
|
||||
case ']':
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
vs := make([]interface{}, len(evals))
|
||||
for i, e := range evals {
|
||||
eval, err := e(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vs[i] = eval
|
||||
}
|
||||
|
||||
return vs, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseJSONObject(c context.Context, p *Parser) (Evaluable, error) {
|
||||
type kv struct {
|
||||
key Evaluable
|
||||
value Evaluable
|
||||
}
|
||||
evals := []kv{}
|
||||
for {
|
||||
switch p.Scan() {
|
||||
default:
|
||||
p.Camouflage("object", ',', '}')
|
||||
key, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.Scan() != ':' {
|
||||
if err != nil {
|
||||
return nil, p.Expected("object", ':')
|
||||
}
|
||||
}
|
||||
value, err := p.ParseExpression(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evals = append(evals, kv{key, value})
|
||||
case ',':
|
||||
case '}':
|
||||
return func(c context.Context, v interface{}) (interface{}, error) {
|
||||
vs := map[string]interface{}{}
|
||||
for _, e := range evals {
|
||||
value, err := e.value(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := e.key.EvalString(c, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vs[key] = value
|
||||
}
|
||||
return vs, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
147
server/vendor/github.com/PaesslerAG/gval/parser.go
generated
vendored
Normal file
147
server/vendor/github.com/PaesslerAG/gval/parser.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package gval
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Parser parses expressions in a Language into an Evaluable
|
||||
type Parser struct {
|
||||
scanner scanner.Scanner
|
||||
Language
|
||||
lastScan rune
|
||||
camouflage error
|
||||
}
|
||||
|
||||
func newParser(expression string, l Language) *Parser {
|
||||
sc := scanner.Scanner{}
|
||||
sc.Init(strings.NewReader(expression))
|
||||
sc.Error = func(*scanner.Scanner, string) {}
|
||||
sc.Filename = expression + "\t"
|
||||
p := &Parser{scanner: sc, Language: l}
|
||||
p.resetScannerProperties()
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Parser) resetScannerProperties() {
|
||||
p.scanner.Whitespace = scanner.GoWhitespace
|
||||
p.scanner.Mode = scanner.GoTokens
|
||||
p.scanner.IsIdentRune = func(r rune, pos int) bool {
|
||||
return unicode.IsLetter(r) || r == '_' || (pos > 0 && unicode.IsDigit(r))
|
||||
}
|
||||
}
|
||||
|
||||
// SetWhitespace sets the behavior of the whitespace matcher. The given
|
||||
// characters must be less than or equal to 0x20 (' ').
|
||||
func (p *Parser) SetWhitespace(chars ...rune) {
|
||||
var mask uint64
|
||||
for _, char := range chars {
|
||||
mask |= 1 << uint(char)
|
||||
}
|
||||
|
||||
p.scanner.Whitespace = mask
|
||||
}
|
||||
|
||||
// SetMode sets the tokens that the underlying scanner will match.
|
||||
func (p *Parser) SetMode(mode uint) {
|
||||
p.scanner.Mode = mode
|
||||
}
|
||||
|
||||
// SetIsIdentRuneFunc sets the function that matches ident characters in the
|
||||
// underlying scanner.
|
||||
func (p *Parser) SetIsIdentRuneFunc(fn func(ch rune, i int) bool) {
|
||||
p.scanner.IsIdentRune = fn
|
||||
}
|
||||
|
||||
// Scan reads the next token or Unicode character from source and returns it.
|
||||
// It only recognizes tokens t for which the respective Mode bit (1<<-t) is set.
|
||||
// It returns scanner.EOF at the end of the source.
|
||||
func (p *Parser) Scan() rune {
|
||||
if p.isCamouflaged() {
|
||||
p.camouflage = nil
|
||||
return p.lastScan
|
||||
}
|
||||
p.camouflage = nil
|
||||
p.lastScan = p.scanner.Scan()
|
||||
return p.lastScan
|
||||
}
|
||||
|
||||
func (p *Parser) isCamouflaged() bool {
|
||||
return p.camouflage != nil && p.camouflage != errCamouflageAfterNext
|
||||
}
|
||||
|
||||
// Camouflage rewind the last Scan(). The Parser holds the camouflage error until
|
||||
// the next Scan()
|
||||
// Do not call Rewind() on a camouflaged Parser
|
||||
func (p *Parser) Camouflage(unit string, expected ...rune) {
|
||||
if p.isCamouflaged() {
|
||||
panic(fmt.Errorf("can only Camouflage() after Scan(): %w", p.camouflage))
|
||||
}
|
||||
p.camouflage = p.Expected(unit, expected...)
|
||||
}
|
||||
|
||||
// Peek returns the next Unicode character in the source without advancing
|
||||
// the scanner. It returns EOF if the scanner's position is at the last
|
||||
// character of the source.
|
||||
// Do not call Peek() on a camouflaged Parser
|
||||
func (p *Parser) Peek() rune {
|
||||
if p.isCamouflaged() {
|
||||
panic("can not Peek() on camouflaged Parser")
|
||||
}
|
||||
return p.scanner.Peek()
|
||||
}
|
||||
|
||||
var errCamouflageAfterNext = fmt.Errorf("Camouflage() after Next()")
|
||||
|
||||
// Next reads and returns the next Unicode character.
|
||||
// It returns EOF at the end of the source.
|
||||
// Do not call Next() on a camouflaged Parser
|
||||
func (p *Parser) Next() rune {
|
||||
if p.isCamouflaged() {
|
||||
panic("can not Next() on camouflaged Parser")
|
||||
}
|
||||
p.camouflage = errCamouflageAfterNext
|
||||
return p.scanner.Next()
|
||||
}
|
||||
|
||||
// TokenText returns the string corresponding to the most recently scanned token.
|
||||
// Valid after calling Scan().
|
||||
func (p *Parser) TokenText() string {
|
||||
return p.scanner.TokenText()
|
||||
}
|
||||
|
||||
// Expected returns an error signaling an unexpected Scan() result
|
||||
func (p *Parser) Expected(unit string, expected ...rune) error {
|
||||
return unexpectedRune{unit, expected, p.lastScan}
|
||||
}
|
||||
|
||||
type unexpectedRune struct {
|
||||
unit string
|
||||
expected []rune
|
||||
got rune
|
||||
}
|
||||
|
||||
func (err unexpectedRune) Error() string {
|
||||
exp := bytes.Buffer{}
|
||||
runes := err.expected
|
||||
switch len(runes) {
|
||||
default:
|
||||
for _, r := range runes[:len(runes)-2] {
|
||||
exp.WriteString(scanner.TokenString(r))
|
||||
exp.WriteString(", ")
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
exp.WriteString(scanner.TokenString(runes[len(runes)-2]))
|
||||
exp.WriteString(" or ")
|
||||
fallthrough
|
||||
case 1:
|
||||
exp.WriteString(scanner.TokenString(runes[len(runes)-1]))
|
||||
case 0:
|
||||
return fmt.Sprintf("unexpected %s while scanning %s", scanner.TokenString(err.got), err.unit)
|
||||
}
|
||||
return fmt.Sprintf("unexpected %s while scanning %s expected %s", scanner.TokenString(err.got), err.unit, exp.String())
|
||||
}
|
||||
BIN
server/vendor/github.com/PaesslerAG/gval/prtg-batmin-gopher.png
generated
vendored
Normal file
BIN
server/vendor/github.com/PaesslerAG/gval/prtg-batmin-gopher.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Reference in New Issue
Block a user