expr: github.com/antonmedv/expr Index | Examples | Files | Directories

package expr

import "github.com/antonmedv/expr"

Index

Examples

Package Files

expr.go

func Compile Uses

func Compile(input string, ops ...Option) (*vm.Program, error)

Compile parses and compiles given input expression to bytecode program.

Code:

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 Uses

func Eval(input string, env interface{}) (interface{}, error)

Eval parses, compiles and runs given input.

Code:

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!

func Run Uses

func Run(program *vm.Program, env interface{}) (interface{}, error)

Run evaluates given bytecode program.

type Option Uses

type Option func(c *conf.Config)

Option for configuring config.

func AllowUndefinedVariables Uses

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.

Code:

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!

func AsBool Uses

func AsBool() Option

AsBool tells the compiler to expect boolean result.

Code:

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

Code:

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 Uses

func AsFloat64() Option

AsFloat64 tells the compiler to expect float64 result.

Code:

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

Code:

_, err := expr.Compile(`!!true`, expr.AsFloat64())

fmt.Printf("%v", err)

Output:

expected float64, but got bool

func AsInt64 Uses

func AsInt64() Option

AsInt64 tells the compiler to expect int64 result.

Code:

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 Uses

func ConstExpr(fn string) Option

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.

Code:

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 Uses

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.

Code:

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 Uses

func Operator(operator string, fn ...string) Option

Operator allows to override binary operator with function.

Code:

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 Optimize Uses

func Optimize(b bool) Option

Optimize turns optimizations on or off.

func Patch Uses

func Patch(visitor ast.Visitor) Option

Patch adds visitor to list of visitors what will be applied before compiling AST to bytecode.

Code:

/*
	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!

Directories

PathSynopsis
ast
checker
compiler
conf
docgen
file
internal/conf
optimizer
parser
parser/lexer
vm
vm/generate

Package expr imports 10 packages (graph) and is imported by 11 packages. Updated 2020-03-27. Refresh now. Tools for package owners.