mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
177 lines
3.8 KiB
Markdown
177 lines
3.8 KiB
Markdown
# Getting Started
|
|
|
|
**Expr** is a simple, fast and extensible expression language for Go. It is
|
|
designed to be easy to use and integrate into your Go application. Let's delve
|
|
deeper into its core features:
|
|
|
|
- **Memory safe** - Designed to prevent vulnerabilities like buffer overflows and memory leaks.
|
|
- **Type safe** - Enforces strict type rules, aligning with Go's type system.
|
|
- **Terminating** - Ensures every expression evaluation cannot loop indefinitely.
|
|
- **Side effects free** - Evaluations won't modify global states or variables.
|
|
|
|
Let's start with a simple example:
|
|
|
|
```go
|
|
program, err := expr.Compile(`2 + 2`)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
output, err := expr.Run(program, nil)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Print(output) // 4
|
|
```
|
|
|
|
Expr compiles the expression `2 + 2` into a bytecode program. Then we run
|
|
the program and get the output.
|
|
|
|
:::tip
|
|
In performance-critical applications, you can reuse the compiled program. Compiled programs are safe for concurrent use.
|
|
**Compile once** and run **multiple** times.
|
|
:::
|
|
|
|
The `expr.Compile` function returns a `*vm.Program` and an error. The `expr.Run` function takes a program and an
|
|
environment. The environment is a map of variables that can be used in the expression. In this example, we use `nil` as
|
|
an environment because we don't need any variables.
|
|
|
|
|
|
Now let's pass some variables to the expression:
|
|
|
|
```go
|
|
env := map[string]any{
|
|
"foo": 100,
|
|
"bar": 200,
|
|
}
|
|
|
|
program, err := expr.Compile(`foo + bar`, expr.Env(env))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
output, err := expr.Run(program, env)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Print(output) // 300
|
|
```
|
|
|
|
Why do we need to pass the environment to the `expr.Compile` function? Expr can be used as a type-safe language.
|
|
Expr can infer the type of the expression and check it against the environment.
|
|
|
|
Here is an example:
|
|
|
|
```go
|
|
env := map[string]any{
|
|
"name": "Anton",
|
|
"age": 35,
|
|
}
|
|
|
|
program, err := expr.Compile(`name + age`, expr.Env(env))
|
|
if err != nil {
|
|
// highlight-next-line
|
|
panic(err) // Will panic with "invalid operation: string + int"
|
|
}
|
|
```
|
|
|
|
Expr can work with any Go types:
|
|
|
|
```go
|
|
env := map[string]any{
|
|
"greet": "Hello, %v!",
|
|
"names": []string{"world", "you"},
|
|
"sprintf": fmt.Sprintf,
|
|
}
|
|
|
|
program, err := expr.Compile(`sprintf(greet, names[0])`, expr.Env(env))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
output, err := expr.Run(program, env)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Print(output) // Hello, world!
|
|
```
|
|
|
|
Also, Expr can use a struct as an environment. Here is an example:
|
|
|
|
```go
|
|
type Env struct {
|
|
Posts []Post `expr:"posts"`
|
|
}
|
|
|
|
func (Env) Format(t time.Time) string { // Methods defined on the struct become functions.
|
|
return t.Format(time.RFC822)
|
|
}
|
|
|
|
type Post struct {
|
|
Body string
|
|
Date time.Time
|
|
}
|
|
|
|
func main() {
|
|
code := `map(posts, Format(.Date) + ": " + .Body)`
|
|
|
|
program, err := expr.Compile(code, expr.Env(Env{})) // Pass the struct as an environment.
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
env := Env{
|
|
Posts: []Post{
|
|
{"Oh My God!", time.Now()},
|
|
{"How you doin?", time.Now()},
|
|
{"Could I be wearing any more clothes?", time.Now()},
|
|
},
|
|
}
|
|
|
|
output, err := expr.Run(program, env)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Print(output)
|
|
}
|
|
```
|
|
|
|
The compiled program can be reused between runs.
|
|
|
|
```go
|
|
type Env struct {
|
|
X int
|
|
Y int
|
|
}
|
|
|
|
program, err := expr.Compile(`X + Y`, expr.Env(Env{}))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
output, err := expr.Run(program, Env{1, 2})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Print(output) // 3
|
|
|
|
output, err = expr.Run(program, Env{3, 4})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Print(output) // 7
|
|
```
|
|
|
|
:::info Eval = Compile + Run
|
|
For one-off expressions, you can use the `expr.Eval` function. It compiles and runs the expression in one step.
|
|
```go
|
|
output, err := expr.Eval(`2 + 2`, env)
|
|
```
|
|
:::
|