Home

Awesome

Build Status Coverage Status GoDoc Go Report Card

It's very common to evaluate an expression dynamicly, so that's why we are here.

S-expression

We use s-epxression syntax to parse and evaluate.

In computing, s-expressions, sexprs or sexps (for "symbolic expression") are a notation for nested list (tree-structured) data, invented for and popularized by the programming language Lisp, which uses them for source code as well as data.

For example, for expression in common way: ā€‹ ( (gender = "female") and
((age % 2) != 0) ) it's coresponding format in s-expression is: ā€‹ (and (= gender "female") (!= (% age 2) 0 ) )

Element types within expression

How to

You can evaluate directly:

params := evaluator.MapParams{
    "gender": "female",
}
res, err := evaluator.EvalBool(`(in gender ("female" "male"))`, params)
if err != nil {
    log.Fatal(err)
}
fmt.Println(res)
# true	

or you can reuse the Expression to evaluate multiple times:

params := evaluator.MapParams{
    "gender": "female",
}
exp, err := evaluator.New(`(in gender ("female" "male"))`)
if err != nil {
    log.Fatal(err)
}
res, err := exp.EvalBool(params)
if err != nil {
    log.Fatal(err)
}
fmt.Println(res)
# true
And you can write expressions like this

Functions

Implemented functions
operandfunctionexampledescription
-in(in 1 (1 2))also suport array like (in (1) ((1)))
-between(between age 18 20)
-overlap(overlap region (3142 1860))
&and(and (eq gender "femal") (between age 18 20))
``or
!not
=eqequal
!=nenot equal
>gtgreater than
<ltless than
>=gegreater than or equal to
<=leless than or equal to
%mod
+-plus
--minus
*-multiply
/-divide
-t_versionconvert type to version
-t_time(t_time "2006-01-02 15:04" "2017-09-09 12:00")convert type to time, first param must be the layout for the time
-td_time(td_time "2017:09:09 12:00:00)convert type to time of default layout format 2006-01-02 15:04:05
_td_date(in (td_date now) (td_date ("2017-01-02" "2017-02-01")) )convert type to time of default layout format 2006-01-02

p.s. either operand or function can be used in expression

How to use self-defined functions

Yes, you can write your own function by following thses steps:

  1. implement your function
  2. regist to functions
  3. enjoy it

here is an example:

package main


import (
	"errors"
	"log"
	"time"

	"github.com/nullne/evaluator"
	"github.com/nullne/evaluator/function"
)

// define your own function and don't forget to register
func age(params ...interface{}) (interface{}, error) {
	if len(params) != 1 {
		return nil, errors.New("only one params accepted")
	}
	birth, ok := params[0].(string)
	if !ok {
		return nil, errors.New("birth format need to be string")
	}
	r, err := time.Parse("2006-01-02", birth)
	if err != nil {
		return nil, err
	}
	now := time.Now()
	a := r.Year() - now.Year()
	if r.Month() < now.Month() {
		a--
	} else if r.Month() == now.Month() {
		if r.Day() < now.Day() {
			a--
		}
	}
	return a, nil
}

func main() {
	if err := function.Regist("age", age); err != nil {
		log.Print(err)
	}

	exp := `(not (between (age birthdate) 18 20))`
	vvf := evaluator.MapParams{
		"birthdate": "1980-02-01",
	}
	e, err := evaluator.New(exp)
	if err != nil {
		log.Print(err)
	}
	r, err := e.Eval(vvf)
	if err != nil {
		log.Print(err)
	}
	log.Printf("expression: `%s`, wanna: %+v, got: %+v\r", exp, true, r)
}

Params

Bench

BenchmarkEqualString-8   	 3000000	       473 ns/op
BenchmarkInString-8      	 2000000	       916 ns/op
BenchmarkBetweenInt-8    	 3000000	       467 ns/op
BenchmarkBetweenTime-8   	 1000000	      2089 ns/op
BenchmarkOverlapInt-8    	  500000	      2966 ns/op
BenchmarkTypeTime-8      	 2000000	       638 ns/op
BenchmarkTypeVersion-8   	 3000000	       539 ns/op

p.s. on MacBook Pro (Retina, 15-inch, Mid 2015), Memory: 16 GB 1600 MHz DDR3, Processor: 2.2 GHz Intel Core i7