Documentation ¶
Index ¶
- func Compile(input string, ops ...Option) (*vm.Program, error)
- func Eval(input string, env interface{}) (interface{}, error)
- func Run(program *vm.Program, env interface{}) (interface{}, error)
- type Option
- func AllowUndefinedVariables() Option
- func AsBool() Option
- func AsFloat64() Option
- func AsInt() Option
- func AsInt64() Option
- func AsKind(kind reflect.Kind) Option
- func ConstExpr(fn string) Option
- func Env(env interface{}) Option
- func Function(name string, fn func(params ...interface{}) (interface{}, error), ...) Option
- func Operator(operator string, fn ...string) Option
- func Optimize(b bool) Option
- func Patch(visitor ast.Visitor) Option
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.
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 a 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 a 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 AsInt ¶ added in v1.10.0
func AsInt() Option
AsInt tells the compiler to expect an int result.
Example ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { program, err := expr.Compile("42", expr.AsInt()) if err != nil { fmt.Printf("%v", err) return } output, err := expr.Run(program, nil) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%T(%v)", output, output) }
Output: int(42)
func AsInt64 ¶ added in v1.2.0
func AsInt64() Option
AsInt64 tells the compiler to expect an 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 AsKind ¶ added in v1.10.0
AsKind tells the compiler to expect kind of the result.
Example ¶
package main import ( "fmt" "reflect" "github.com/antonmedv/expr" ) func main() { program, err := expr.Compile("{a: 1, b: 2}", expr.AsKind(reflect.Map)) 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) }
Output: map[a:1 b:2]
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
Example (Tagged_field_names) ¶
package main import ( "fmt" "github.com/antonmedv/expr" ) func main() { env := struct { FirstWord string Separator string `expr:"Space"` SecondWord string `expr:"second_word"` }{ FirstWord: "Hello", Separator: " ", SecondWord: "World", } output, err := expr.Eval(`FirstWord + Space + second_word`, env) if err != nil { fmt.Printf("%v", err) return } fmt.Printf("%v", output) // Output : Hello World }
Output:
func Function ¶ added in v1.11.0
func Function(name string, fn func(params ...interface{}) (interface{}, error), types ...interface{}) Option
Function adds function to list of functions what will be available in expressions.
func Operator ¶ added in v1.2.0
Operator allows to replace a binary operator with a 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" "github.com/antonmedv/expr/ast" ) func main() { /* type patcher struct{} func (p *patcher) Visit(node *ast.Node) { switch n := (*node).(type) { case *ast.MemberNode: ast.Patch(node, &ast.CallNode{ Callee: &ast.IdentifierNode{Value: "get"}, Arguments: []ast.Node{n.Node, 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) Visit(node *ast.Node) { switch n := (*node).(type) { case *ast.MemberNode: ast.Patch(node, &ast.CallNode{ Callee: &ast.IdentifierNode{Value: "get"}, Arguments: []ast.Node{n.Node, n.Property}, }) } }
Output: