Documentation
¶
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Compile ¶ added in v1.2.0
Compile parses and compiles given input expression to bytecode program.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { env := map[string]interface{}{ "foo": 1, "bar": 99, } program, err := expr.Compile("foo in 1..99 and bar in 1..99", expr.Env(env)) if err != nil { fmt.Printf("%v", err) return } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output) }
Output: true
func Eval ¶
Eval parses, compiles and runs given input.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { output, err := expr.Eval("greet + name", map[string]interface{}{ "greet": "Hello, ", "name": "world!", }) if err != nil { fmt.Printf("err: %v", err) return } fmt.Printf("%v", output) }
Output: Hello, world!
Example (Runtime_error) ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { _, err := expr.Eval(`map(1..3, {1 / (# - 3)})`, nil) fmt.Print(err) }
Output: runtime error: integer divide by zero (1:14) | map(1..3, {1 / (# - 3)}) | .............^
Types ¶
type Option ¶ added in v1.3.1
Option for configuring config.
func AllowUndefinedVariables ¶ added in v1.4.0
func AllowUndefinedVariables() Option
AllowUndefinedVariables allows to use undefined variables inside expressions. This can be used with expr.Env option to partially define a few variables. Note what this option is only works in map environment are used, otherwise runtime.fetch will panic as there is no way to get missing field zero value.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { code := `name == nil ? "Hello, world!" : sprintf("Hello, %v!", name)` env := map[string]interface{}{ "sprintf": fmt.Sprintf, } options := []expr.Option{ expr.Env(env), expr.AllowUndefinedVariables(), // Allow to use undefined variables. } program, err := expr.Compile(code, options...) if err != nil { fmt.Printf("%v", err) return } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v\n", output) env["name"] = "you" // Define variables later on. output, err = expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v\n", output) }
Output: Hello, world! Hello, you!
Example (Zero_value) ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { code := `name == "" ? foo + bar : foo + name` // If environment has different zero values, then undefined variables // will have it as default value. env := map[string]string{} options := []expr.Option{ expr.Env(env), expr.AllowUndefinedVariables(), // Allow to use undefined variables. } program, err := expr.Compile(code, options...) if err != nil { fmt.Printf("%v", err) return } env = map[string]string{ "foo": "Hello, ", "bar": "world!", } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output) }
Output: Hello, world!
Example (Zero_value_functions) ¶
package main import ( "fmt" "strings" "github.com/antonmedv/expr" ) func main() { code := `words == "" ? Split("foo,bar", ",") : Split(words, ",")` // Env is map[string]string type on which methods are defined. env := mockMapStringStringEnv{} options := []expr.Option{ expr.Env(env), expr.AllowUndefinedVariables(), // Allow to use undefined variables. } program, err := expr.Compile(code, options...) if err != nil { fmt.Printf("%v", err) return } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output) } type mockMapStringStringEnv map[string]string func (m mockMapStringStringEnv) Split(s, sep string) []string { return strings.Split(s, sep) }
Output: [foo bar]
func AsBool ¶ added in v1.2.0
func AsBool() Option
AsBool tells the compiler to expect boolean result.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { env := map[string]int{ "foo": 0, } program, err := expr.Compile("foo >= 0", expr.Env(env), expr.AsBool()) if err != nil { fmt.Printf("%v", err) return } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output.(bool)) }
Output: true
Example (Error) ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { env := map[string]interface{}{ "foo": 0, } _, err := expr.Compile("foo + 42", expr.Env(env), expr.AsBool()) fmt.Printf("%v", err) }
Output: expected bool, but got int
func AsFloat64 ¶ added in v1.2.0
func AsFloat64() Option
AsFloat64 tells the compiler to expect float64 result.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { program, err := expr.Compile("42", expr.AsFloat64()) if err != nil { fmt.Printf("%v", err) return } output, err := expr.Run(program, nil) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output.(float64)) }
Output: 42
Example (Error) ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { _, err := expr.Compile(`!!true`, expr.AsFloat64()) fmt.Printf("%v", err) }
Output: expected float64, but got bool
func AsInt64 ¶ added in v1.2.0
func AsInt64() Option
AsInt64 tells the compiler to expect int64 result.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { env := map[string]interface{}{ "rating": 5.5, } program, err := expr.Compile("rating", expr.Env(env), expr.AsInt64()) if err != nil { fmt.Printf("%v", err) return } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output.(int64)) }
Output: 5
func ConstExpr ¶ added in v1.6.0
ConstExpr defines func expression as constant. If all argument to this function is constants, then it can be replaced by result of this func call on compile step.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func fib(n int) int { if n <= 1 { return n } return fib(n-1) + fib(n-2) } func main() { code := `[fib(5), fib(3+3), fib(dyn)]` env := map[string]interface{}{ "fib": fib, "dyn": 0, } options := []expr.Option{ expr.Env(env), expr.ConstExpr("fib"), // Mark fib func as constant expression. } program, err := expr.Compile(code, options...) if err != nil { fmt.Printf("%v", err) return } // Only fib(5) and fib(6) calculated on Compile, fib(dyn) can be called at runtime. env["dyn"] = 7 output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v\n", output) }
Output: [5 8 13]
func Env ¶ added in v1.0.7
func Env(env interface{}) Option
Env specifies expected input of env for type checks. If struct is passed, all fields will be treated as variables, as well as all fields of embedded structs and struct itself. If map is passed, all items will be treated as variables. Methods defined on this type will be available as functions.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { type Segment struct { Origin string } type Passengers struct { Adults int } type Meta struct { Tags map[string]string } type Env struct { Meta Segments []*Segment Passengers *Passengers Marker string } code := `all(Segments, {.Origin == "MOW"}) && Passengers.Adults > 0 && Tags["foo"] startsWith "bar"` program, err := expr.Compile(code, expr.Env(Env{})) if err != nil { fmt.Printf("%v", err) return } env := Env{ Meta: Meta{ Tags: map[string]string{ "foo": "bar", }, }, Segments: []*Segment{ {Origin: "MOW"}, }, Passengers: &Passengers{ Adults: 2, }, Marker: "test", } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output) }
Output: true
func Operator ¶ added in v1.2.0
Operator allows to override binary operator with function.
Example ¶
package main import ( "fmt" "time" "github.com/antonmedv/expr" ) func main() { code := ` Now() > CreatedAt && (Now() - CreatedAt).Hours() > 24 ` type Env struct { CreatedAt time.Time Now func() time.Time Sub func(a, b time.Time) time.Duration After func(a, b time.Time) bool } options := []expr.Option{ expr.Env(Env{}), expr.Operator(">", "After"), expr.Operator("-", "Sub"), } program, err := expr.Compile(code, options...) if err != nil { fmt.Printf("%v", err) return } env := Env{ CreatedAt: time.Date(2018, 7, 14, 0, 0, 0, 0, time.UTC), Now: func() time.Time { return time.Now() }, Sub: func(a, b time.Time) time.Duration { return a.Sub(b) }, After: func(a, b time.Time) bool { return a.After(b) }, } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output) }
Output: true
func Patch ¶ added in v1.7.0
Patch adds visitor to list of visitors what will be applied before compiling AST to bytecode.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr/ast" "github.com/antonmedv/expr" ) func main() { /* type patcher struct{} func (p *patcher) Enter(_ *ast.Node) {} func (p *patcher) Exit(node *ast.Node) { switch n := (*node).(type) { case *ast.PropertyNode: ast.Patch(node, &ast.FunctionNode{ Name: "get", Arguments: []ast.Node{n.Node, &ast.StringNode{Value: n.Property}}, }) } } */ program, err := expr.Compile( `greet.you.world + "!"`, expr.Patch(&patcher{}), ) if err != nil { fmt.Printf("%v", err) return } env := map[string]interface{}{ "greet": "Hello", "get": func(a, b string) string { return a + ", " + b }, } output, err := expr.Run(program, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output) // Output : Hello, you, world! } type patcher struct{} func (p *patcher) Enter(_ *ast.Node) {} func (p *patcher) Exit(node *ast.Node) { switch n := (*node).(type) { case *ast.PropertyNode: ast.Patch(node, &ast.FunctionNode{ Name: "get", Arguments: []ast.Node{n.Node, &ast.StringNode{Value: n.Property}}, }) } }
Output: