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 (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 ---------------^