mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2025-10-13 02:53:52 +08:00
261 lines
6.0 KiB
Go
261 lines
6.0 KiB
Go
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
|
|
}
|