Skip to content

Commit 6171d56

Browse files
committed
Update docs
1 parent 83d67e7 commit 6171d56

14 files changed

+495
-402
lines changed

docs/configuration.md

+90-111
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,135 @@
11
# Configuration
22

3-
Expr can be configured with options. For example, you can pass the environment with variables and functions.
3+
## Return type
44

5-
## AllowUndefinedVariables()
5+
Usually, the return type of expression is anything. But we can instruct type checker to verify the return type of the
6+
expression.
7+
For example, in filter expressions, we expect the return type to be a boolean.
68

7-
This option allows undefined variables in the expression. By default, Expr will return an error
8-
if the expression contains undefined variables.
9+
```go
10+
program, err := expr.Compile(code, expr.AsBool())
11+
if err != nil {
12+
panic(err)
13+
}
914

10-
```go
11-
program, err := expr.Compile(`foo + bar`, expr.AllowUndefinedVariables())
12-
```
15+
output, err := expr.Run(program, env)
16+
if err != nil {
17+
panic(err)
18+
}
1319

14-
## AsBool()
20+
ok := output.(bool) // It is safe to assert the output to bool, if the expression is type checked as bool.
21+
```
1522

16-
This option forces the expression to return a boolean value. If the expression returns a non-boolean value,
17-
Expr will return an error.
23+
If `code` variable for example returns a string, the compiler will return an error.
1824

19-
```go
20-
program, err := expr.Compile(`Title contains "Hello"`, expr.AsBool())
21-
```
25+
Expr has a few options to specify the return type:
2226

23-
## AsFloat64()
27+
- [expr.AsBool()](https://pkg.go.dev/github.com/expr-lang/expr#AsBool) - expects the return type to be a bool.
28+
- [expr.AsInt()](https://pkg.go.dev/github.com/expr-lang/expr#AsInt) - expects the return type to be an int (float64,
29+
uint, int32, and other will be cast to int).
30+
- [expr.AsInt64()](https://pkg.go.dev/github.com/expr-lang/expr#AsInt64) - expects the return type to be an int64 (
31+
float64, uint, int32, and other will be cast to int64).
32+
- [expr.AsFloat64()](https://pkg.go.dev/github.com/expr-lang/expr#AsFloat64) - expects the return type to be a float64 (
33+
float32 will be cast to float64).
34+
- [expr.AsAny()](https://pkg.go.dev/github.com/expr-lang/expr#AsAny) - expects the return type to be anything.
35+
- [expr.AsKind(reflect.Kind)](https://pkg.go.dev/github.com/expr-lang/expr#AsKind) - expects the return type to be a
36+
specific kind.
2437

25-
This option forces the expression to return a float64 value. If the expression returns a non-float64 value,
26-
Expr will return an error.
38+
:::tip Warn on any
39+
By default, type checker will accept any type, even if the return type is specified. Consider following examples:
2740

28-
```go
29-
program, err := expr.Compile(`42`, expr.AsFloat64())
41+
```expr
42+
let arr = [1, 2, 3]; arr[0]
3043
```
3144

32-
:::note
33-
If the expression returns integer value, Expr will convert it to float64.
34-
:::
35-
36-
## AsInt()
45+
The return type of the expression is `any`. Arrays created in Expr are of type `[]any`. The type checker will not return
46+
an error if the return type is specified as `expr.AsInt()`. The output of the expression is `1`, which is an int, but the
47+
type checker will not return an error.
3748

38-
This option forces the expression to return an int value. If the expression returns a non-int value,
39-
Expr will return an error.
49+
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.
4050

4151
```go
42-
program, err := expr.Compile(`42`, expr.AsInt())
52+
program, err := expr.Compile(code, expr.AsInt(), expr.WarnOnAny())
4353
```
4454

45-
:::note
46-
If the expression returns a float value, Expr truncates it to int.
47-
:::
48-
49-
## AsInt64()
55+
The type checker will return an error if the return type is `any`. We need to modify the expression to return a specific
56+
type.
5057

51-
Same as `AsInt()` but returns an int64 value.
52-
53-
```go
54-
program, err := expr.Compile(`42`, expr.AsInt64())
58+
```expr
59+
let arr = [1, 2, 3]; int(arr[0])
5560
```
61+
:::
5662

57-
## AsKind()
5863

59-
This option forces the expression to return a value of the specified kind.
60-
If the expression returns a value of a different kind, Expr will return an error.
64+
## WithContext
6165

62-
```go
63-
program, err := expr.Compile(`42`, expr.AsKind(reflect.String))
64-
```
66+
Although the compiled program is guaranteed to be terminated, some user defined functions may not be. For example, if a
67+
user defined function calls a remote service, we may want to pass a context to the function.
6568

66-
## ConstExpr()
69+
This is possible via the [`WithContext`](https://pkg.go.dev/github.com/expr-lang/expr#WithContext) option.
6770

68-
This option tells Expr to treat specified functions as constant expressions.
69-
If all arguments of the function are constants, Expr will replace the function call with the result
70-
during the compile step.
71+
This option will modify function calls to include the context as the first argument (only if the function signature
72+
accepts a context).
7173

72-
```go
73-
program, err := expr.Compile(`fib(42)`, expr.ConstExpr("fib"))
74+
```expr
75+
customFunc(42)
76+
// will be transformed to
77+
customFunc(ctx, 42)
7478
```
7579

76-
[ConstExpr Example](https://pkg.go.dev/github.com/expr-lang/expr?tab=doc#ConstExpr)
77-
78-
## Env()
79-
80-
This option passes the environment with variables and functions to the expression.
80+
Function `expr.WithContext()` takes the name of context variable. The context variable must be defined in the environment.
8181

8282
```go
83-
program, err := expr.Compile(`foo + bar`, expr.Env(Env{}))
83+
env := map[string]any{
84+
"ctx": context.Background(),
85+
}
86+
87+
program, err := expr.Compile(code, expr.Env(env), expr.WithContext("ctx"))
8488
```
8589

86-
## Function()
90+
## ConstExpr
8791

88-
This option adds a function to the expression.
92+
For some user defined functions, we may want to evaluate the expression at compile time. This is possible via the
93+
[`ConstExpr`](https://pkg.go.dev/github.com/expr-lang/expr#ConstExpr) option.
8994

9095
```go
91-
atoi := expr.Function(
92-
"atoi",
93-
func(params ...any) (any, error) {
94-
return strconv.Atoi(params[0].(string))
95-
},
96-
)
97-
98-
program, err := expr.Compile(`atoi("42")`, atoi)
96+
func fib(n int) int {
97+
if n <= 1 {
98+
return n
99+
}
100+
return fib(n-1) + fib(n-2)
101+
}
102+
103+
env := map[string]any{
104+
"fib": fib,
105+
}
106+
107+
program, err := expr.Compile(`fib(10)`, expr.Env(env), expr.ConstExpr("fib"))
99108
```
100109

101-
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
102-
signature or multiple signatures.
110+
If all arguments of the function are constants, the function will be evaluated at compile time. The result of the function
111+
will be used as a constant in the expression.
103112

104-
```go
105-
atoi := expr.Function(
106-
"atoi",
107-
func(params ...any) (any, error) {
108-
return strconv.Atoi(params[0].(string))
109-
},
110-
new(func(string) int),
111-
)
113+
```expr
114+
fib(10) // will be transformed to 55 during the compilation
115+
fib(12+12) // will be transformed to 267914296 during the compilation
116+
fib(x) // will **not** be transformed and will be evaluated at runtime
112117
```
113118

114-
Or we can simply reuse the `strconv.Atoi` function.
115-
116-
```go
117-
atoi := expr.Function(
118-
"atoi",
119-
func(params ...any) (any, error) {
120-
return strconv.Atoi(params[0].(string))
121-
},
122-
strconv.Atoi,
123-
)
124-
```
119+
## Options
125120

126-
Here is another example with a few function signatures:
121+
Compiler options can be defined as an array:
127122

128123
```go
129-
toInt := expr.Function(
130-
"toInt",
131-
func(params ...any) (any, error) {
132-
switch params[0].(type) {
133-
case float64:
134-
return int(params[0].(float64)), nil
135-
case string:
136-
return strconv.Atoi(params[0].(string))
137-
}
138-
return nil, fmt.Errorf("invalid type")
139-
},
140-
new(func(float64) int),
141-
new(func(string) int),
142-
)
124+
options := []expr.Option{
125+
expr.Env(Env{})
126+
expr.AsInt(),
127+
expr.WarnOnAny(),
128+
expr.WithContext("ctx"),
129+
expr.ConstExpr("fib"),
130+
}
131+
132+
program, err := expr.Compile(code, options...)
143133
```
144134

145-
146-
## Operator()
147-
148-
This options defines an [operator overloading](operator-overloading).
149-
150-
## Optimize()
151-
152-
This option enables [optimizations](internals.md). By default, Expr will optimize the expression.
153-
154-
## Patch()
155-
156-
This option allows you to [patch the expression](visitor-and-patch) before compilation.
135+
Full list of available options can be found in the [pkg.go.dev](https://pkg.go.dev/github.com/expr-lang/expr#Option) documentation.

docs/environment.md

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Environment
2+
3+
The environment is a map or a struct that contains the variables and functions that the expression can access.
4+
5+
## Struct as Environment
6+
7+
Let's consider the following example:
8+
9+
```go
10+
type Env struct {
11+
UpdatedAt time.Time
12+
Posts []Post
13+
Map map[string]string `expr:"tags"`
14+
}
15+
```
16+
17+
The `Env` struct contains 3 variables that the expression can access: `UpdatedAt`, `Posts`, and `tags`.
18+
19+
:::info
20+
The `expr` tag is used to rename the `Map` field to `tags` variable in the expression.
21+
:::
22+
23+
The `Env` struct can also contain methods. The methods defined on the struct become functions that the expression can
24+
call.
25+
26+
```go
27+
func (Env) Format(t time.Time) string {
28+
return t.Format(time.RFC822)
29+
}
30+
```
31+
32+
:::tip
33+
Methods defined on embedded structs are also accessible.
34+
```go
35+
type Env struct {
36+
Helpers
37+
}
38+
39+
type Helpers struct{}
40+
41+
func (Helpers) Format(t time.Time) string {
42+
return t.Format(time.RFC822)
43+
}
44+
```
45+
:::
46+
47+
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
48+
the fields and methods of the struct.
49+
50+
```go
51+
program, err := expr.Compile(code, expr.Env(Env{}))
52+
```
53+
54+
Compiler will type check the expression against the environment. After the compilation, we can run the program with the environment.
55+
You should use the same type of environment that you passed to the `expr.Env` function.
56+
57+
```go
58+
output, err := expr.Run(program, Env{
59+
UpdatedAt: time.Now(),
60+
Posts: []Post{{Title: "Hello, World!"}},
61+
Map: map[string]string{"tag1": "value1"},
62+
})
63+
```
64+
65+
## Map as Environment
66+
67+
You can also use a map as an environment.
68+
69+
```go
70+
env := map[string]any{
71+
"UpdatedAt": time.Time{},
72+
"Posts": []Post{},
73+
"tags": map[string]string{},
74+
"sprintf": fmt.Sprintf,
75+
}
76+
77+
program, err := expr.Compile(code, expr.Env(env))
78+
```
79+
80+
A map defines variables and functions that the expression can access. The key is the variable name, and the type
81+
is the value's type.
82+
83+
```go
84+
env := map[string]any{
85+
"object": map[string]any{
86+
"field": 42,
87+
},
88+
}
89+
```
90+
91+
Expr will infer the type of the `object` variable as `map[string]any`.
92+
93+
By default, Expr will return an error if unknown variables are used in the expression.
94+
95+
You can disable this behavior by passing [`AllowUndefinedVariables`](https://pkg.go.dev/github.com/expr-lang/expr#AllowUndefinedVariables) option to the compiler.

0 commit comments

Comments
 (0)