Expression evaluation engine for Go: fast, non-Turing complete, dynamic typing, static typing

Overview

Expr

Build Status Go Report Card GoDoc

expr logo

Expr package provides an engine that can compile and evaluate expressions. An expression is a one-liner that returns a value (mostly, but not limited to, booleans). It is designed for simplicity, speed and safety.

The purpose of the package is to allow users to use expressions inside configuration for more complex logic. It is a perfect candidate for the foundation of a business rule engine. The idea is to let configure things in a dynamic way without recompile of a program:

# Get the special price if
user.Group in ["good_customers", "collaborator"]

# Promote article to the homepage when
len(article.Comments) > 100 and article.Category not in ["misc"]

# Send an alert when
product.Stock < 15

Features

  • Seamless integration with Go (no need to redefine types)
  • Static typing (example).
    out, err := expr.Compile(`name + age`)
    // err: invalid operation + (mismatched types string and int)
    // | name + age
    // | .....^
  • User-friendly error messages.
  • Reasonable set of basic operators.
  • Builtins all, none, any, one, filter, map.
    all(Tweets, {.Size <= 280})
  • Fast (benchmarks): uses bytecode virtual machine and optimizing compiler.

Install

go get github.com/antonmedv/expr

Documentation

Expr Code Editor

Expr Code Editor

Also, I have an embeddable code editor written in JavaScript which allows editing expressions with syntax highlighting and autocomplete based on your types declaration.

Learn more →

Examples

Play Online

package main

import (
	"fmt"
	"github.com/antonmedv/expr"
)

func main() {
	env := map[string]interface{}{
		"greet":   "Hello, %v!",
		"names":   []string{"world", "you"},
		"sprintf": fmt.Sprintf,
	}

	code := `sprintf(greet, names[0])`

	program, err := expr.Compile(code, expr.Env(env))
	if err != nil {
		panic(err)
	}

	output, err := expr.Run(program, env)
	if err != nil {
		panic(err)
	}

	fmt.Println(output)
}

Play Online

package main

import (
	"fmt"
	"github.com/antonmedv/expr"
)

type Tweet struct {
	Len int
}

type Env struct {
	Tweets []Tweet
}

func main() {
	code := `all(Tweets, {.Len <= 240})`

	program, err := expr.Compile(code, expr.Env(Env{}))
	if err != nil {
		panic(err)
	}

	env := Env{
		Tweets: []Tweet{{42}, {98}, {69}},
	}
	output, err := expr.Run(program, env)
	if err != nil {
		panic(err)
	}

	fmt.Println(output)
}

Contributing

Expr consist of a few packages for parsing source code to AST, type checking AST, compiling to bytecode and VM for running bytecode program.

Also expr provides powerful tool exe for debugging. It has interactive terminal debugger for our bytecode virtual machine.

debugger

Who is using Expr?

  • Aviasales Aviasales are actively using Expr for different parts of the search engine.
  • Argo Argo Rollouts - Progressive Delivery for Kubernetes.
  • Argo Argo Workflows - The workflow engine for KubernetesOverview.
  • CrowdSec Crowdsec - A security automation tool.
  • Mystery Minds uses Expr to allow easy yet powerful customization of its matching algorithm.

Add your company too

License

MIT

Issues
  • WIP: try marshall

    WIP: try marshall

    #52

    opened by rucciva 17
  • Parser error on 386 arch by int overflows

    Parser error on 386 arch by int overflows

    Windows 7 32-bit go1.11.2 windows/386 Trying to do:

    package main
    
    import (
    	"github.com/antonmedv/expr"
    )
    
    func main() {
    	p, err := expr.Compile("1+2") 
    }
    
    

    Build fails with multiple errors:

    Installation failed: # github.com/antonmedv/expr/parser/gen
    ..\..\..\..\github.com\antonmedv\expr\parser\gen\expr_parser.go:2231: constant 4228579072 overflows int
    ..\..\..\..\github.com\antonmedv\expr\parser\gen\expr_parser.go:2249: constant 4228579072 overflows int
    ..\..\..\..\github.com\antonmedv\expr\parser\gen\expr_parser.go:2300: constant 4228579072 overflows int
    
    
    bug 
    opened by r3code 13
  • A new type named set(contain no repeated value) different from the built-in type map

    A new type named set(contain no repeated value) different from the built-in type map

    Thanks for your contribution, this library help me a lot. Now, I receive a requirement from colleagues, he needs a type "set". The built-in type "map" all contains repeated value, but I want a type that like "map", but without repeating elements, just like type “set” in java, how do i add a new type “set” in this “expr”. @antonmedv

    the new type "set" equal to "map[string]struct{}" in golang

    opened by xiaoyang-chen 13
  • JSON marshaling of Program is incomplete

    JSON marshaling of Program is incomplete

    The documentation states that marshaling and unmarshaling of a program is supported. However, the following test fails:

    func Test_marshalRegexp(t *testing.T) {
    	prog, err := expr.Compile(`"hello" matches "h.*"`)
    	if !assert.NoError(t, err) {
    		t.FailNow()
    	}
    
    	marshaled, err := json.Marshal(prog)
    	if !assert.NoError(t, err) {
    		t.FailNow()
    	}
    
    	unmarshaledProgram := &vm.Program{}
    	err = json.Unmarshal(marshaled, unmarshaledProgram)
    	if !assert.NoError(t, err) {
    		t.FailNow()
    	}
    
    	output, err := expr.Run(unmarshaledProgram, nil)
    	if !assert.NoError(t, err) {
    		t.FailNow()
    	}
    	assert.Equal(t, true, output)
    }
    
    bug good first issue 
    opened by bontibon 11
  • Add ability to return errors from custom functions.

    Add ability to return errors from custom functions.

    Also provide initial documentation on using and providing functions.

    opened by ffenix113 9
  • vm.go: reflect calls with nil parameters fix

    vm.go: reflect calls with nil parameters fix

    golang gotcha: reflect calls with nil params fail without this trick

    opened by wintermute-cds 9
  • Match string in string

    Match string in string

    Adds ability to match if "foo" in "foobar"

    opened by relvacode 9
  • Another go.mod v2 issue?

    Another go.mod v2 issue?

    We have been using v1.1.4 of the 'expr' package, which is great, by the way. We finally updated to GO 1.13 and at the same time started migrating to GO modules.

    However, when upgrading, our process is failing because of a missing "/v2" in your go.mod module path.

    $ go get github.com/antonmedv/[email protected]
    go: finding github.com v2.1.1
    go: finding github.com/antonmedv v2.1.1
    go: finding github.com/antonmedv/expr v2.1.1
    go: finding github.com/antonmedv/expr v2.1.1
    go get github.com/antonmedv/[email protected]: github.com/antonmedv/[email protected]: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2
    

    I was able to download the source and modify the "go.mod" file to be:

    $ cat go.mod
    module github.com/antonmedv/expr/v2
    
    go 1.12
    
    require (
    	github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b
    	github.com/gdamore/tcell v1.1.2
    	github.com/rivo/tview v0.0.0-20190515161233-bd836ef13b4b
    	github.com/sanity-io/litter v1.1.0
    	github.com/stretchr/testify v1.3.0
    	golang.org/x/text v0.3.2
    )
    

    This helped me get past the above error.

    Is there something I am doing wrong? Or is this an issue with the "expr" module. I am new to "go modules" and dealing with the learning curve.

    opened by starbuck-ms 9
  • vm: Use a preallocated fixed call frame for typical argument sizes

    vm: Use a preallocated fixed call frame for typical argument sizes

    For calls with 20 or less arguments use a preallocated fixed array for the calling frame, otherwise fall back to heap allocation. This avoids what was previously always a heap allocation at callsites.

    Before: Benchmark_Calls-4 50000 27618 ns/op

    After: Benchmark_Calls-4 50000 25948 ns/op

    opened by deanm 8
  • Question

    Question

    Hello guys!

    Would like to do simple assignation évaluation, something like:

    Var lib string Code = ‘lib="ok"’ ... expr code to parse this thing... Font.Println(lib) // should print ok

    Is it something doable? If yes what code should i use to achieve that?

    opened by jscoys 8
  • Does expr support the '&' operator

    Does expr support the '&' operator

    val := `1 & 1`
    complie, err := expr.Compile(val)
    r, err := expr.Run(complie,env)
    if err!=nil {
    fmt.Printf("%+v", err)
    }
    if v, ok := r.(int); ok {
    fmt.Printf("%+v", v)
    }
    

    image

    opened by ShengXiangXiong 3
  • Sandboxing expression

    Sandboxing expression

    We want to be able to sandbox expression evaluation, because some users might submit time, memory, or CPU intensive expression, either accidentally, or as a intentional attack of code that uses expr, example

    map(0..10000, {sprig.genPrivateKey('rsa')}
    
    opened by alexec 17
  • Fetcher Interface

    Fetcher Interface

    How exactly should this be implemented:

    To fetch fields from struct, values from map, get by indexes expr uses reflect package. Envs can implement vm.Fetcher interface, to avoid use reflect:

    type Fetcher interface {
    	Fetch(interface{}) interface{}
    }
    

    I mean considering the following struct .. how does one implement this optimization?

    type foo struct {
    i int,
    f float64,
    s string
    }
    

    Thank you.

    opened by seekerkoruth 0
  • sugar or syntax for date or datetime?

    sugar or syntax for date or datetime?

    e.g. 5 min ago in expr, or 2 days long.

    opened by loveyou127 5
  • Any way to make numbers/result float64 by default?

    Any way to make numbers/result float64 by default?

    Hi. Is there any way in code to make the expression evaluate all static numbers as floats rather than ints? Am using expr to allow end-users to enter simple expressions in places where numbers would normally be used, but if they do "5/2" they get (int)2 rather than (float)2.5. It would be too hard to explain to all users of the software that they should use "5.0/2". Thanks!

    opened by simon-anz 2
  • How to compare type like `type Status string`?

    How to compare type like `type Status string`?

    In a nutshell, compare between string and type Other string will cause error like interface conversion: interface {} is main.Status, not string. Any elegant way to result this issue?

    Type define:

    type Status string
    
    const (
    	Success Status = "Success"
    	Failure Status = "Failure"
    )
    
    func (s Status) String() string {
    	if s == Success {
    		return "✅"
    	} else if s == Failure {
    		return "❎"
    	} else {
    		return "👃🏻"
    	}
    }
    

    Main code:

    env := map[string]interface{}{
        "okay": Success,
        "fail": Failure,
        "str":  func(v interface{}) string { return fmt.Sprint(v) },
    }
    queries := []string{
        `true`,
        `false`,
        `str(okay)`,
        `str(okay) == "Success"`,
        `okay`,
        `okay == "Success"`,
        `str(okay) == "✅"`,
    }
    for _, query := range queries {
        prog, err := expr.Compile(query, expr.Env(env))
        if err != nil {
            log.Warnw("failed to compile query", "query", query, zap.Error(err))
            continue
        }
    
        var output interface{}
        if output, err = expr.Run(prog, env); err != nil {
            log.Warnw("failed to execute query", "query", query, zap.Error(err))
            continue
        } else {
            log.Infow("run successfully", "output", output)
        }
    }
    

    Result:

    2021-08-27T18:25:04.805+0800	INFO	runexpr/main.go:57	run successfully	{"pid": 77239, "query": "true", "output": true}
    2021-08-27T18:25:04.805+0800	INFO	runexpr/main.go:57	run successfully	{"pid": 77239, "query": "false", "output": false}
    2021-08-27T18:25:04.806+0800	INFO	runexpr/main.go:57	run successfully	{"pid": 77239, "query": "str(okay)", "output": "✅"}
    2021-08-27T18:25:04.806+0800	INFO	runexpr/main.go:57	run successfully	{"pid": 77239, "query": "str(okay) == \"Success\"", "output": false}
    2021-08-27T18:25:04.806+0800	INFO	runexpr/main.go:57	run successfully	{"pid": 77239, "query": "okay", "output": "✅"}
    2021-08-27T18:25:04.806+0800	WARN	runexpr/main.go:54	failed to execute query	{"pid": 77239, "query": "okay == \"Success\"", "error": "interface conversion: interface {} is main.Status, not string (1:6)\n | okay == \"Success\"\n | .....^"}
    2021-08-27T18:25:04.806+0800	INFO	runexpr/main.go:57	run successfully	{"pid": 77239, "query": "str(okay) == \"✅\"", "output": true}
    
    opened by vip-king 1
  • Explain mode

    Explain mode

    I'd like to explain to users how a result was calculated:

    Say we have this expression, this is what it would print out:

    • c + p / e + P / (n * e)
    • 2 + 100 / 250 + 2500 / (10 * 250)
    • 2 + 0 + 1
    • 3
    opened by alexec 2
  • Helper methods to handle time?

    Helper methods to handle time?

    It's kinda hard to compare and calculate time in expr, any ideas to improve this?

    opened by Robucon 0
  • Map Access does not have check

    Map Access does not have check

    I am trying to put a check in map access.

    if you have a Golang map foo and it does not have an element "bar" the code below will fail silently without an error message regarding non existent map element. It returns a 0 and the silent nature of this substitution and error can create issues.

    foo.bar+1

    I suggest in line 37 of runtime.go:

    	case reflect.Map:
    		value := v.MapIndex(reflect.ValueOf(i))
    		if value.IsValid() {
    			if value.CanInterface() {
    				return value.Interface()
    			}
    		} else {
    			panic(fmt.Sprintf("%v not found", i))        // this is the addition
    			//elem := reflect.TypeOf(from).Elem()       // this is commented
    			//return reflect.Zero(elem).Interface()       // this is commented
    		}
    
    

    Thank you.

    opened by seekerkoruth 4
  • Timeout

    Timeout

    Is there a way to timeout and kill a running expr.Run/VM.Run process if it is taking too long? This is to terminate/interupt runaway code that was miscoded,etc. I did a string search for use of golang context and did not see any.

    Thank you.

    opened by seekerkoruth 2
Releases(v1.9.0)
Owner
Anton Medvedev
curl medv.io
Anton Medvedev
Expression evaluation in golang

Gval Gval (Go eVALuate) provides support for evaluating arbitrary expressions, in particular Go-like expressions. Evaluate Gval can evaluate expressio

null 398 Dec 1, 2021
Duktape JavaScript engine bindings for Go

Duktape bindings for Go(Golang) Duktape is a thin, embeddable javascript engine. Most of the api is implemented. The exceptions are listed here. Usage

Oleg Lebedev 779 Nov 23, 2021
ECMAScript/JavaScript engine in pure Go

Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and performance.

Dmitry Panov 2.5k Nov 25, 2021
A fast script language for Go

The Tengo Language Tengo is a small, dynamic, fast, secure script language for Go. Tengo is fast and secure because it's compiled/executed as bytecode

daniel 2.5k Nov 26, 2021
🦁 A Super fast and lightweight runtime for JavaScript Scripts

Kimera.js - A super fast and lightweight JavaScript Runtime for Scripts.

Eliaz Bobadilla 4 Nov 4, 2021
Expression evaluation engine for Go: fast, non-Turing complete, dynamic typing, static typing

Expr Expr package provides an engine that can compile and evaluate expressions. An expression is a one-liner that returns a value (mostly, but not lim

Anton Medvedev 2.1k Nov 30, 2021
Expression evaluation engine for Go: fast, non-Turing complete, dynamic typing, static typing

Expr Expr package provides an engine that can compile and evaluate expressions. An expression is a one-liner that returns a value (mostly, but not lim

Anton Medvedev 2.1k Nov 25, 2021
Fast, portable, non-Turing complete expression evaluation with gradual typing (Go)

Common Expression Language The Common Expression Language (CEL) is a non-Turing complete language designed for simplicity, speed, safety, and portabil

Google 979 Nov 29, 2021
Fast, portable, non-Turing complete expression evaluation with gradual typing (Go)

Common Expression Language The Common Expression Language (CEL) is a non-Turing complete language designed for simplicity, speed, safety, and portabil

Google 982 Dec 1, 2021
Simple expression evaluation engine for Go

??️ chili Currently in development, Unstable (API may change in future) Simple expression evaluation engine. Expression is one liner that evalutes int

Santhosh Kumar 65 Jun 6, 2021
Expression evaluation in golang

Gval Gval (Go eVALuate) provides support for evaluating arbitrary expressions, in particular Go-like expressions. Evaluate Gval can evaluate expressio

null 398 Dec 1, 2021
Expression evaluation in golang

Gval Gval (Go eVALuate) provides support for evaluating arbitrary expressions, in particular Go-like expressions. Evaluate Gval can evaluate expressio

null 396 Nov 26, 2021
Arbitrary expression evaluation for golang

govaluate Provides support for evaluating arbitrary C-like artithmetic/string expressions. Why can't you just write these expressions in code? Sometim

George Lester 2.1k Dec 5, 2021
The Dual-Stack Dynamic DNS client, the world's first dynamic DNS client built for IPv6.

dsddns DsDDNS is the Dual-Stack Dynamic DNS client. A dynamic DNS client keeps your DNS records in sync with the IP addresses associated with your hom

Ryan Young 4 Sep 11, 2021
An implementation of Neural Turing Machines

Neural Turing Machines Package ntm implements the Neural Turing Machine architecture as described in A.Graves, G. Wayne, and I. Danihelka. arXiv prepr

Fumin 396 Sep 4, 2021
Mathematical expression parsing and calculation engine library. 数学表达式解析计算引擎库

Math-Engine 使用 Go 实现的数学表达式解析计算引擎库,它小巧,无任何依赖,具有扩展性(比如可以注册自己的函数到引擎中),比较完整的完成了数学表达式解析执行,包括词法分析、语法分析、构建AST、运行。 go get -u github.com/dengsgo/math-engine 能够

Deng.Liu 176 Nov 23, 2021
A complete Liquid template engine in Go

Liquid Template Parser liquid is a pure Go implementation of Shopify Liquid templates. It was developed for use in the Gojekyll port of the Jekyll sta

Oliver Steele 142 Dec 5, 2021
A complete Liquid template engine in Go

Liquid Template Parser liquid is a pure Go implementation of Shopify Liquid templates. It was developed for use in the Gojekyll port of the Jekyll sta

Oliver Steele 142 Dec 5, 2021
🚀Gev is a lightweight, fast non-blocking TCP network library based on Reactor mode. Support custom protocols to quickly and easily build high-performance servers.

gev 中文 | English gev is a lightweight, fast non-blocking TCP network library based on Reactor mode. Support custom protocols to quickly and easily bui

徐旭 1.3k Nov 30, 2021
An extremely fast Go (golang) HTTP router that supports regular expression route matching. Comes with full support for building RESTful APIs.

ozzo-routing You may consider using go-rest-api to jumpstart your new RESTful applications with ozzo-routing. Description ozzo-routing is a Go package

Ozzo Framework 420 Dec 1, 2021
Lightweight, fast and dependency-free Cron expression parser (due checker) for Golang (tested on v1.13 and above)

adhocore/gronx gronx is Golang cron expression parser ported from adhocore/cron-expr. Zero dependency. Very fast because it bails early in case a segm

Jitendra Adhikari 180 Nov 30, 2021
Serverless SOAR (Security Orchestration, Automation and Response) framework for automatic inspection and evaluation of security alert

DeepAlert DeepAlert is a serverless framework for automatic response of security alert. Overview DeepAlert receives a security alert that is event of

null 29 Dec 1, 2021
A Golang program for a colleague to help in calculating the ratio between the points obtained in a test and the corresponding evaluation in tenths.

A Golang program for a colleague to help in calculating the ratio between the points obtained in a test and the corresponding evaluation in tenths. If you have not the compiled file (.exe) you can build it with the Go compiler.

Francesco Fontana 0 Nov 15, 2021
⚙️ Concept of Golang HTML render engine with frontend components and dynamic behavior

An HTML render engine concept that brings frontend-like components experience to the server side with native html/template on steroids. Supports any s

Yurii Zinets 381 Nov 29, 2021
⚙️ Concept of Golang HTML render engine with frontend components and dynamic behavior

SSC Engine An HTML render engine concept that brings frontend-like components experience to the server side with native html/template on steroids. Sup

Yurii Zinets 382 Dec 1, 2021
Crane - 🐦 A full-text WebAssembley search engine for static websites

Crane ?? My blog post: WebAssembly Search Tools for Static Sites Crane is a technical demo is inspired by Stork and uses a near-identical configuratio

Andrew Healey 28 Aug 28, 2021
Super fast static photo and video gallery generator (written in Go and HTML/CSS/native JS)

fastgallery Fast static photo and video gallery generator Super fast (written in Go and C, concurrent, uses fastest image/video libraries, 4-8 times f

Toni Melisma 20 Jul 13, 2021
Fast Static File Analysis Framework

Florentino; Fast Static File Analysis Framework Story Florentino is named after a fiction warrior. Flarentino: "I'd wear a fedora but they haven't inv

null 96 Jul 9, 2021