Documentation
¶
Overview ¶
Package expr is an engine that can evaluate expressions.
// Evaluate expression on data.
result, err := expr.Eval("expression", data)
// Or precompile expression to ast first.
node, err := expr.Parse("expression")
// And run later.
result, err := expr.Run(node, data)
Passing in Variables ¶
You can pass variables into the expression, which can be of any valid Go type (including structs):
// Maps
data := map[string]interface{}{
"Foo": ...
"Bar": ...
}
// Structs
data := Payload{
Foo: ...
Bar: ...
}
// Pass object
result, err := expr.Eval("Foo == Bar", data)
Expr uses reflection for accessing and iterating passed data. For example you can pass nested structures without any modification or preparation:
type Cookie struct {
Key string
Value string
}
type User struct {
UserAgent string
Cookies []Cookie
}
type Request struct {
User user
}
req := Request{User{
Cookies: []Cookie{{"origin", "www"}},
UserAgent: "Firefox",
}}
ok, err := expr.Eval(`User.UserAgent matches "Firefox" and User.Cookies[0].Value == "www"`, req)
Passing in Functions ¶
You can also pass functions into the expression:
data := map[string]interface{}{
"Request": req,
"Values": func(xs []Cookie) []string {
vs := make([]string, 0)
for _, x := range xs {
vs = append(vs, x.Value)
}
return vs
},
}
ok, err := expr.Eval(`"www" in Values(Request.User.Cookies)`, data)
Caching ¶
If you planning to execute some expression lots times, it's good to compile it first and only one time:
// Precompile node, err := expr.Parse(expression) // Run ok, err := expr.Run(node, data)
Checking variables and functions ¶
It is possible to check used variables and functions during parsing of the expression.
expression := `Request.User.UserAgent matches "Firefox" && "www" in Values(Request.User.Cookies)`
node, err := expr.Parse(expression, expr.Names("Request"), expr.Funcs("Values"))
Only `Request` and `Values` will be available inside expression, otherwise parse error.
If you try to use some undeclared variables or functions, an error will be returned during compilation:
expression := `Unknown(Request.User.UserAgent)`
node, err := expr.Parse(expression, expr.Names("Request"), expr.Funcs("Values"))
// err.Error():
unknown func Unknown
Unknown(Request.User.UserAgent)
-------^
Printing ¶
Compiled ast can be compiled back to string expression using stringer fmt.Stringer interface:
node, err := expr.Parse(expression)
code := fmt.Sprintf("%v", node)
Number type ¶
Inside Expr engine there is no distinguish between int, uint and float types (as in JavaScript). All numbers inside Expr engine represented as `float64`. You should remember about it if you use any of binary operators (`+`, `-`, `/`, `*`, etc). Otherwise type remain unchanged.
data := map[string]int{
"Foo": 1,
"Bar": 2,
}
out, err := expr.Eval(`Foo`, data) // int
out, err := expr.Eval(`Foo + Bar`, data) // float64
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Eval ¶
Eval parses and evaluates given input.
Example ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
output, err := expr.Eval("'hello world'", nil)
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", output)
}
Output: hello world
Example (Error) ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
output, err := expr.Eval("(boo + bar]", nil)
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", output)
}
Output: err: unclosed "(" (boo + bar] ----------^
Example (Map) ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
"strings"
)
func main() {
env := map[string]interface{}{
"foo": 1,
"bar": []string{"zero", "hello world"},
"swipe": func(in string) string {
return strings.Replace(in, "world", "user", 1)
},
}
output, err := expr.Eval("swipe(bar[foo])", env)
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", output)
}
Output: hello user
Example (Matches) ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
output, err := expr.Eval(`"a" matches "a("`, nil)
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", output)
}
Output: err: error parsing regexp: missing closing ): `a(` "a" matches "a(" ----------------^
Example (Struct) ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
type C struct{ C int }
type B struct{ B *C }
type A struct{ A B }
env := A{B{&C{42}}}
output, err := expr.Eval("A.B.C", env)
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", output)
}
Output: 42
func Run ¶
Run evaluates given ast.
Example ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
env := map[string]interface{}{
"foo": 1,
"bar": 99,
}
ast, err := expr.Parse("foo + bar not in 99..100")
if err != nil {
fmt.Printf("err: %v", err)
return
}
output, err := expr.Run(ast, env)
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", output)
}
Output: false
Types ¶
type Node ¶
type Node interface{}
Node represents items of abstract syntax tree.
Example ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
node, err := expr.Parse("foo.bar")
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", node)
}
Output: foo.bar
func Parse ¶
Parse parses input into ast.
Example ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
env := map[string]interface{}{
"foo": 1,
"bar": 99,
}
ast, err := expr.Parse("foo in 1..99 and bar in 1..99")
if err != nil {
fmt.Printf("err: %v", err)
return
}
output, err := expr.Run(ast, env)
if err != nil {
fmt.Printf("err: %v", err)
return
}
fmt.Printf("%v", output)
}
Output: true
type OptionFn ¶
type OptionFn func(p *options)
OptionFn for configuring parser.
func Funcs ¶
Funcs sets list of allowed function.
Example ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
_, err := expr.Parse("foo(bar(baz()))", expr.Funcs("foo", "bar"))
if err != nil {
fmt.Printf("err: %v", err)
return
}
}
Output: err: unknown func baz foo(bar(baz())) --------^
func Names ¶
Names sets list of allowed names.
Example ¶
package main
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
_, err := expr.Parse("foo + bar + baz", expr.Names("foo", "bar"))
if err != nil {
fmt.Printf("err: %v", err)
return
}
}
Output: err: unknown name baz foo + bar + baz ------------^
