Skip to content

Commit db05fa8

Browse files
committed
Add readme
1 parent 3bf88d0 commit db05fa8

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

README.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Expr
2+
3+
Expr is an engine that can evaluate expressions.
4+
5+
The purpose of the package is to allow users to use expressions inside configuration for more complex logic.
6+
It is a perfect candidate for the foundation of a _business rule engine_.
7+
The idea is to let configure things in a dynamic way without recompile of a program:
8+
9+
```ruby
10+
# Get the special price if
11+
user.Group() in ["good_customers", "collaborator"]
12+
13+
# Promote article to the homepage when
14+
article.CommentCount > 100 and article.Category not in ["misc"]
15+
16+
# Send an alert when
17+
product.Stock < 15
18+
```
19+
20+
Inspired by
21+
* Symfony's [The ExpressionLanguage](https://github.com/symfony/expression-language) component,
22+
* Rob Pike's talk [Lexical Scanning in Go](https://talks.golang.org/2011/lex.slide).
23+
24+
## Install
25+
26+
```
27+
go get -u github.com/antonmedv/expr
28+
```
29+
30+
## Documentation
31+
32+
### Usage
33+
```go
34+
// Evaluate expression on data.
35+
result, err := expr.Eval("expression", data)
36+
37+
// Or precompile expression to ast first.
38+
node, err := expr.Parse("expression")
39+
40+
// And run later.
41+
result, err := expr.Run(node, data)
42+
```
43+
44+
### Expression Syntax
45+
See [The Expression Syntax](https://github.com/antonmedv/expr/wiki/The-Expression-Syntax) to learn the syntax of the Expr expressions.
46+
47+
### Passing in Variables
48+
You can pass variables into the expression, which can be of any valid Go type (including structs):
49+
```go
50+
// Maps
51+
data := map[string]interface{}{
52+
"Foo": ...
53+
"Bar": ...
54+
}
55+
56+
// Structs
57+
data := Payload{
58+
Foo: ...
59+
Bar: ...
60+
}
61+
62+
// Pass object
63+
result, err := expr.Eval("Foo == Bar", data)
64+
```
65+
66+
Expr uses reflection for accessing and iterating passed data.
67+
For example you can pass nested structures without any modification or preparation:
68+
69+
```go
70+
type Cookie struct {
71+
Key string
72+
Value string
73+
}
74+
type User struct {
75+
UserAgent string
76+
Cookies []Cookie
77+
}
78+
type Request struct {
79+
User user
80+
}
81+
82+
req := Request{User{
83+
Cookies: []cookie{{"origin", "www"}},
84+
UserAgent: "Firefox",
85+
}}
86+
87+
ok, err := expr.Eval(`User.UserAgent matches "Firefox" and User.Cookies[0].Value == "www"`, req)
88+
```
89+
90+
### Passing in Functions
91+
You can also pass functions into the expression:
92+
```go
93+
data := map[string]interface{}{
94+
"Request": req,
95+
"Values": func(xs []Cookie) []string {
96+
vs := make([]string, 0)
97+
for _, x := range xs {
98+
vs = append(vs, x.Value)
99+
}
100+
return vs
101+
},
102+
}
103+
104+
ok, err := expr.Eval(`"www" in Values(Request.User.Cookies)`, data)
105+
```
106+
107+
### Caching
108+
If you planning to execute some expression lots times, it's good to compile it first and only one time:
109+
110+
```go
111+
// Precompile
112+
node, err := expr.Parse(expression)
113+
114+
// Run
115+
ok, err := expr.Run(node, data)
116+
```
117+
118+
### Checking variables and functions
119+
It is possible to check used variables and functions during parsing of the expression.
120+
121+
```go
122+
expression := `Request.User.UserAgent matches "Firefox" && "www" in Values(Request.User.Cookies)`
123+
124+
node, err := expr.Parse(expression, expr.Names("Request"), expr.Funcs("Values"))
125+
```
126+
127+
Only `Request` and `Values` will bbe available inside expression, otherwise parse error.
128+
129+
### Printing
130+
Compiled ast can be compiled back to string expression using _String()_:
131+
132+
```go
133+
node, err := expr.Parse(expression)
134+
code := fmt.Sprintf("%v", node)
135+
```
136+
137+
### Number type
138+
Inside Expr engine there is no distinguish between int, uint and float types (as in JavaScript).
139+
All numbers inside Expr engine represented as `float64`.
140+
You should remember about it if you use any of binary operators (`+`, `-`, `/`, `*`, etc).
141+
Otherwise type remain unchanged.
142+
143+
```go
144+
data := map[string]int{
145+
"Foo": 1,
146+
"Bar": 2,
147+
}
148+
149+
out, err := expr.Eval(`Foo`, data) // int
150+
151+
out, err := expr.Eval(`Foo + Bar`, data) // float64
152+
```
153+
154+
## License
155+
156+
MIT

0 commit comments

Comments
 (0)