-
-
Notifications
You must be signed in to change notification settings - Fork 405
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
495 additions
and
402 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,156 +1,135 @@ | ||
# Configuration | ||
|
||
Expr can be configured with options. For example, you can pass the environment with variables and functions. | ||
## Return type | ||
|
||
## AllowUndefinedVariables() | ||
Usually, the return type of expression is anything. But we can instruct type checker to verify the return type of the | ||
expression. | ||
For example, in filter expressions, we expect the return type to be a boolean. | ||
|
||
This option allows undefined variables in the expression. By default, Expr will return an error | ||
if the expression contains undefined variables. | ||
```go | ||
program, err := expr.Compile(code, expr.AsBool()) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
```go | ||
program, err := expr.Compile(`foo + bar`, expr.AllowUndefinedVariables()) | ||
``` | ||
output, err := expr.Run(program, env) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
## AsBool() | ||
ok := output.(bool) // It is safe to assert the output to bool, if the expression is type checked as bool. | ||
``` | ||
|
||
This option forces the expression to return a boolean value. If the expression returns a non-boolean value, | ||
Expr will return an error. | ||
If `code` variable for example returns a string, the compiler will return an error. | ||
|
||
```go | ||
program, err := expr.Compile(`Title contains "Hello"`, expr.AsBool()) | ||
``` | ||
Expr has a few options to specify the return type: | ||
|
||
## AsFloat64() | ||
- [expr.AsBool()](https://pkg.go.dev/github.com/expr-lang/expr#AsBool) - expects the return type to be a bool. | ||
- [expr.AsInt()](https://pkg.go.dev/github.com/expr-lang/expr#AsInt) - expects the return type to be an int (float64, | ||
uint, int32, and other will be cast to int). | ||
- [expr.AsInt64()](https://pkg.go.dev/github.com/expr-lang/expr#AsInt64) - expects the return type to be an int64 ( | ||
float64, uint, int32, and other will be cast to int64). | ||
- [expr.AsFloat64()](https://pkg.go.dev/github.com/expr-lang/expr#AsFloat64) - expects the return type to be a float64 ( | ||
float32 will be cast to float64). | ||
- [expr.AsAny()](https://pkg.go.dev/github.com/expr-lang/expr#AsAny) - expects the return type to be anything. | ||
- [expr.AsKind(reflect.Kind)](https://pkg.go.dev/github.com/expr-lang/expr#AsKind) - expects the return type to be a | ||
specific kind. | ||
|
||
This option forces the expression to return a float64 value. If the expression returns a non-float64 value, | ||
Expr will return an error. | ||
:::tip Warn on any | ||
By default, type checker will accept any type, even if the return type is specified. Consider following examples: | ||
|
||
```go | ||
program, err := expr.Compile(`42`, expr.AsFloat64()) | ||
```expr | ||
let arr = [1, 2, 3]; arr[0] | ||
``` | ||
|
||
:::note | ||
If the expression returns integer value, Expr will convert it to float64. | ||
::: | ||
|
||
## AsInt() | ||
The return type of the expression is `any`. Arrays created in Expr are of type `[]any`. The type checker will not return | ||
an error if the return type is specified as `expr.AsInt()`. The output of the expression is `1`, which is an int, but the | ||
type checker will not return an error. | ||
|
||
This option forces the expression to return an int value. If the expression returns a non-int value, | ||
Expr will return an error. | ||
But we can instruct the type checker to warn us if the return type is `any`. Use [`expr.WarnOnAny()`](https://pkg.go.dev/github.com/expr-lang/expr#WarnOnAny) to enable this behavior. | ||
|
||
```go | ||
program, err := expr.Compile(`42`, expr.AsInt()) | ||
program, err := expr.Compile(code, expr.AsInt(), expr.WarnOnAny()) | ||
``` | ||
|
||
:::note | ||
If the expression returns a float value, Expr truncates it to int. | ||
::: | ||
|
||
## AsInt64() | ||
The type checker will return an error if the return type is `any`. We need to modify the expression to return a specific | ||
type. | ||
|
||
Same as `AsInt()` but returns an int64 value. | ||
|
||
```go | ||
program, err := expr.Compile(`42`, expr.AsInt64()) | ||
```expr | ||
let arr = [1, 2, 3]; int(arr[0]) | ||
``` | ||
::: | ||
|
||
## AsKind() | ||
|
||
This option forces the expression to return a value of the specified kind. | ||
If the expression returns a value of a different kind, Expr will return an error. | ||
## WithContext | ||
|
||
```go | ||
program, err := expr.Compile(`42`, expr.AsKind(reflect.String)) | ||
``` | ||
Although the compiled program is guaranteed to be terminated, some user defined functions may not be. For example, if a | ||
user defined function calls a remote service, we may want to pass a context to the function. | ||
|
||
## ConstExpr() | ||
This is possible via the [`WithContext`](https://pkg.go.dev/github.com/expr-lang/expr#WithContext) option. | ||
|
||
This option tells Expr to treat specified functions as constant expressions. | ||
If all arguments of the function are constants, Expr will replace the function call with the result | ||
during the compile step. | ||
This option will modify function calls to include the context as the first argument (only if the function signature | ||
accepts a context). | ||
|
||
```go | ||
program, err := expr.Compile(`fib(42)`, expr.ConstExpr("fib")) | ||
```expr | ||
customFunc(42) | ||
// will be transformed to | ||
customFunc(ctx, 42) | ||
``` | ||
|
||
[ConstExpr Example](https://pkg.go.dev/github.com/expr-lang/expr?tab=doc#ConstExpr) | ||
|
||
## Env() | ||
|
||
This option passes the environment with variables and functions to the expression. | ||
Function `expr.WithContext()` takes the name of context variable. The context variable must be defined in the environment. | ||
|
||
```go | ||
program, err := expr.Compile(`foo + bar`, expr.Env(Env{})) | ||
env := map[string]any{ | ||
"ctx": context.Background(), | ||
} | ||
|
||
program, err := expr.Compile(code, expr.Env(env), expr.WithContext("ctx")) | ||
``` | ||
|
||
## Function() | ||
## ConstExpr | ||
|
||
This option adds a function to the expression. | ||
For some user defined functions, we may want to evaluate the expression at compile time. This is possible via the | ||
[`ConstExpr`](https://pkg.go.dev/github.com/expr-lang/expr#ConstExpr) option. | ||
|
||
```go | ||
atoi := expr.Function( | ||
"atoi", | ||
func(params ...any) (any, error) { | ||
return strconv.Atoi(params[0].(string)) | ||
}, | ||
) | ||
|
||
program, err := expr.Compile(`atoi("42")`, atoi) | ||
func fib(n int) int { | ||
if n <= 1 { | ||
return n | ||
} | ||
return fib(n-1) + fib(n-2) | ||
} | ||
|
||
env := map[string]any{ | ||
"fib": fib, | ||
} | ||
|
||
program, err := expr.Compile(`fib(10)`, expr.Env(env), expr.ConstExpr("fib")) | ||
``` | ||
|
||
Expr sees the `atoi` function as a function with a variadic number of arguments of type `any` and returns a value of type `any`. But, we can specify the types of arguments and the return value by adding the correct function | ||
signature or multiple signatures. | ||
If all arguments of the function are constants, the function will be evaluated at compile time. The result of the function | ||
will be used as a constant in the expression. | ||
|
||
```go | ||
atoi := expr.Function( | ||
"atoi", | ||
func(params ...any) (any, error) { | ||
return strconv.Atoi(params[0].(string)) | ||
}, | ||
new(func(string) int), | ||
) | ||
```expr | ||
fib(10) // will be transformed to 55 during the compilation | ||
fib(12+12) // will be transformed to 267914296 during the compilation | ||
fib(x) // will **not** be transformed and will be evaluated at runtime | ||
``` | ||
|
||
Or we can simply reuse the `strconv.Atoi` function. | ||
|
||
```go | ||
atoi := expr.Function( | ||
"atoi", | ||
func(params ...any) (any, error) { | ||
return strconv.Atoi(params[0].(string)) | ||
}, | ||
strconv.Atoi, | ||
) | ||
``` | ||
## Options | ||
|
||
Here is another example with a few function signatures: | ||
Compiler options can be defined as an array: | ||
|
||
```go | ||
toInt := expr.Function( | ||
"toInt", | ||
func(params ...any) (any, error) { | ||
switch params[0].(type) { | ||
case float64: | ||
return int(params[0].(float64)), nil | ||
case string: | ||
return strconv.Atoi(params[0].(string)) | ||
} | ||
return nil, fmt.Errorf("invalid type") | ||
}, | ||
new(func(float64) int), | ||
new(func(string) int), | ||
) | ||
options := []expr.Option{ | ||
expr.Env(Env{}) | ||
expr.AsInt(), | ||
expr.WarnOnAny(), | ||
expr.WithContext("ctx"), | ||
expr.ConstExpr("fib"), | ||
} | ||
|
||
program, err := expr.Compile(code, options...) | ||
``` | ||
|
||
|
||
## Operator() | ||
|
||
This options defines an [operator overloading](operator-overloading). | ||
|
||
## Optimize() | ||
|
||
This option enables [optimizations](internals.md). By default, Expr will optimize the expression. | ||
|
||
## Patch() | ||
|
||
This option allows you to [patch the expression](visitor-and-patch) before compilation. | ||
Full list of available options can be found in the [pkg.go.dev](https://pkg.go.dev/github.com/expr-lang/expr#Option) documentation. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# Environment | ||
|
||
The environment is a map or a struct that contains the variables and functions that the expression can access. | ||
|
||
## Struct as Environment | ||
|
||
Let's consider the following example: | ||
|
||
```go | ||
type Env struct { | ||
UpdatedAt time.Time | ||
Posts []Post | ||
Map map[string]string `expr:"tags"` | ||
} | ||
``` | ||
|
||
The `Env` struct contains 3 variables that the expression can access: `UpdatedAt`, `Posts`, and `tags`. | ||
|
||
:::info | ||
The `expr` tag is used to rename the `Map` field to `tags` variable in the expression. | ||
::: | ||
|
||
The `Env` struct can also contain methods. The methods defined on the struct become functions that the expression can | ||
call. | ||
|
||
```go | ||
func (Env) Format(t time.Time) string { | ||
return t.Format(time.RFC822) | ||
} | ||
``` | ||
|
||
:::tip | ||
Methods defined on embedded structs are also accessible. | ||
```go | ||
type Env struct { | ||
Helpers | ||
} | ||
|
||
type Helpers struct{} | ||
|
||
func (Helpers) Format(t time.Time) string { | ||
return t.Format(time.RFC822) | ||
} | ||
``` | ||
::: | ||
|
||
We can use an empty struct `Env{}` to with [expr.Env](https://pkg.go.dev/github.com/expr-lang/expr#Env) to create an environment. Expr will use reflection to find | ||
the fields and methods of the struct. | ||
|
||
```go | ||
program, err := expr.Compile(code, expr.Env(Env{})) | ||
``` | ||
|
||
Compiler will type check the expression against the environment. After the compilation, we can run the program with the environment. | ||
You should use the same type of environment that you passed to the `expr.Env` function. | ||
|
||
```go | ||
output, err := expr.Run(program, Env{ | ||
UpdatedAt: time.Now(), | ||
Posts: []Post{{Title: "Hello, World!"}}, | ||
Map: map[string]string{"tag1": "value1"}, | ||
}) | ||
``` | ||
|
||
## Map as Environment | ||
|
||
You can also use a map as an environment. | ||
|
||
```go | ||
env := map[string]any{ | ||
"UpdatedAt": time.Time{}, | ||
"Posts": []Post{}, | ||
"tags": map[string]string{}, | ||
"sprintf": fmt.Sprintf, | ||
} | ||
|
||
program, err := expr.Compile(code, expr.Env(env)) | ||
``` | ||
|
||
A map defines variables and functions that the expression can access. The key is the variable name, and the type | ||
is the value's type. | ||
|
||
```go | ||
env := map[string]any{ | ||
"object": map[string]any{ | ||
"field": 42, | ||
}, | ||
} | ||
``` | ||
|
||
Expr will infer the type of the `object` variable as `map[string]any`. | ||
|
||
By default, Expr will return an error if unknown variables are used in the expression. | ||
|
||
You can disable this behavior by passing [`AllowUndefinedVariables`](https://pkg.go.dev/github.com/expr-lang/expr#AllowUndefinedVariables) option to the compiler. |
Oops, something went wrong.