Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nullne/evaluator
https://github.com/nullne/evaluator
evaluation evaluator expression golang s-expressions
Last synced: about 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/nullne/evaluator
- Owner: nullne
- License: mit
- Created: 2017-04-27T18:31:46.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2023-04-14T11:05:55.000Z (over 1 year ago)
- Last Synced: 2024-07-31T20:53:32.802Z (5 months ago)
- Topics: evaluation, evaluator, expression, golang, s-expressions
- Language: Go
- Size: 51.8 KB
- Stars: 39
- Watchers: 3
- Forks: 8
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-go - evaluator - Evaluate an expression dynamically based on s-expression. It's simple and easy to extend. (Utilities / Utility/Miscellaneous)
- awesome-go - evaluator - ★ 10 (Utilities)
- awesome-go-extra - evaluator - 04-27T18:31:46Z|2021-07-25T13:59:51Z| (Utilities / Fail injection)
README
[![Build Status](https://travis-ci.org/nullne/evaluator.svg?branch=master)](https://travis-ci.org/nullne/evaluator.svg?branch=master)
[![Coverage Status](https://coveralls.io/repos/github/nullne/evaluator/badge.svg)](https://coveralls.io/github/nullne/evaluator)
[![GoDoc](https://godoc.org/github.com/nullne/evaluator?status.svg)](http://godoc.org/github.com/nullne/evaluator)
[![Go Report Card](https://goreportcard.com/badge/github.com/nullne/evaluator)](https://goreportcard.com/report/github.com/nullne/evaluator)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
- number
For convenience, we treat float64, int64 and so on as type of number. For example, float `100.0` is equal to int `100`, but not euqal to string `"100"`- string
character string quoted with `` ` ``, `'`, or `"` are treated as type of `string`. You can convert type `string` to any other defined type you like by type convert functions which are mentioned later- function or variable
character string without quotes are regarded as type of `function` or `variable` which depends on whether this function exists. For example in expression `(age birthdate)`, both `age` and `birthdate` is unquoted. `age` is type of function because we have registered a function named `age`, while `birthdate` is type of variable for not found. The program will come to errors if there is neither parameter nor function named `birthdate` when evaluating#### 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)
# trueor 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
- `(in gender ("male", "female"))`
- `(between now (td_time "2017-01-02 12:00:00") (td_time "2017-12-02 12:00:00"))`
- `(ne (mod (age birthdate) 7) 5)`
- or multiple-line for clarity```
(and
(ne os "ios")
(eq gender "male")
(beteen version (t_version "2.7.1") (t_version "2.9.1"))
)
```#### Functions
##### Implemented functions| operand | function | example | description
| ------- | --------- | ---------- | ----
| - | `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` |
| `=` | `eq` | | equal
| `!=` | `ne` | | not equal
| `>` | `gt` | | greater than
| `<` | `lt` | | less than
| `>=` | `ge` | | greater than or equal to
| `<=` | `le` | | less than or equal to
| `%` | `mod` |
| `+` | - | | plus
| `-` | - | | minus
| `*` | - | | multiply
| `/` | - | | divide
| - | `t_version` || convert 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 ithere is an example:
```go
package mainimport (
"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
- `Params` interface, which has a method named `Get` to get all params needed
- `MapParams` a simple implemented `Params` in `map`#### 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/opp.s. on MacBook Pro (Retina, 15-inch, Mid 2015), Memory: 16 GB 1600 MHz DDR3, Processor: 2.2 GHz Intel Core i7