Skip to content

Commit c943380

Browse files
committed
Add Getting-Started.md
1 parent a142a71 commit c943380

File tree

4 files changed

+143
-2
lines changed

4 files changed

+143
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ go get github.com/antonmedv/expr
5555

5656
## Documentation
5757

58-
* See [docs](docs) page for developer documentation.
58+
* See [Getting Started](docs/Getting-Started.md) page for developer documentation.
5959
* See [Language Definition](docs/Language-Definition.md) page to learn the syntax.
6060

6161
## Examples

docs/Getting-Started.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Getting Started
2+
3+
**Expr** provides a package for evaluating arbitrary expressions as well as type checking of such expression.
4+
For demonstration purpose, let's assume that we already have some types and structs describing our data. And we want to implement filtering of data flow, via providing our users to configure such filters via expressions.
5+
6+
```go
7+
type Requset struct {
8+
Location string
9+
Date time.Time
10+
Ticket Ticket
11+
}
12+
13+
type Ticket struct {
14+
Segments []Segment
15+
}
16+
17+
type Segment struct {
18+
Origin, Destination string
19+
Date time.Time
20+
}
21+
```
22+
23+
First what we need to do is to create a way users will be creating, editing (and maybe deleted) filter rules, but this is out of scope for this article. Let's assume that we have some kind of Web interface where users can do all of this task. On creation or deletion of rules, we much check if rules are written correctly. And we want to give the user access to `Request` fields and only these fields.
24+
25+
```go
26+
import (
27+
"github.com/antonmedv/expr"
28+
"github.com/antonmedv/expr/vm"
29+
)
30+
31+
var program *vm.Program
32+
33+
program, err = expr.Compile(rule, expr.Env(&Request{}))
34+
```
35+
36+
By passing `&Request{}` as env into compile method we turned on type checking, now if users make an error in field or compare strings with integers, he will get an error. Why we used reverence type will be explained later, when we add functions. Now users can save expressions.
37+
38+
```coffeescript
39+
all(Ticket.Segments, {.Origin == Location}) and Date.Before(Ticket.Segments[0].Date)
40+
```
41+
42+
Now we need to implement the execution of our compiled program. On each request, we have some kind of filter loop.
43+
44+
```go
45+
output, err := expr.Run(program, requset)
46+
if err != nil {
47+
return err
48+
}
49+
50+
if !output.(bool) {
51+
continue
52+
}
53+
```
54+
55+
Now let's some add a function for repetitive tasks.
56+
57+
```go
58+
func (r *Requset) SameLocation() bool {
59+
same := false
60+
for _, s := range r.Ticket.Segments {
61+
same = same && s.Origin == r.Location
62+
}
63+
return same
64+
}
65+
```
66+
67+
Now users can use functions inside expressions.
68+
69+
```coffeescript
70+
SameLocation() and Date.Before(Ticket.Segments[0].Date)
71+
```
72+
73+
Much better. But using time's package methods isn't pretty. What if we can override operators? And we can! Let's describe another function.
74+
75+
```go
76+
func (*Requset) Before(a, b time.Time) bool {
77+
return a.Before(b)
78+
}
79+
```
80+
81+
Now, on compile time, override `<` operator.
82+
83+
```go
84+
program, err = expr.Compile(rule, expr.Env(&Request{}), expr.Operator("<", "Before"))
85+
```
86+
87+
That's it! Now users can write expressions in a more pleasant way. Other operators `>`, `===`, etc can be overridden if similar way.
88+
89+
```coffeescript
90+
SameLocation() and Date < Ticket.Segments[0].Date
91+
```
92+
93+
Next
94+
* [Language Definition](Language-Definition.md)
95+
* [Usage](Usage.md)

docs/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Documentation
22

3-
* [Usage](Usage.md)
3+
* [Getting Started](Getting-Started.md)
44
* [Language Definition](Language-Definition.md)
5+
* [Usage](Usage.md)

expr_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,51 @@ func ExampleOperator() {
230230
// Output: true
231231
}
232232

233+
func ExampleOperator_time() {
234+
type Segment struct {
235+
Date time.Time
236+
}
237+
type Request struct {
238+
Segments []Segment
239+
Before func(a, b time.Time) bool
240+
Date func(s string) time.Time
241+
}
242+
243+
code := `Date("2001-01-01") < Segments[0].Date`
244+
245+
program, err := expr.Compile(code, expr.Env(&Request{}), expr.Operator("<", "Before"))
246+
if err != nil {
247+
fmt.Printf("%v", err)
248+
return
249+
}
250+
251+
request := &Request{
252+
Segments: []Segment{
253+
{Date: time.Date(2019, 7, 1, 0, 0, 0, 0, time.UTC)},
254+
},
255+
Before: func(a, b time.Time) bool {
256+
return a.Before(b)
257+
},
258+
Date: func(s string) time.Time {
259+
date, err := time.Parse("2006-01-02", s)
260+
if err != nil {
261+
panic(err)
262+
}
263+
return date
264+
},
265+
}
266+
267+
output, err := expr.Run(program, request)
268+
if err != nil {
269+
fmt.Printf("%v", err)
270+
return
271+
}
272+
273+
fmt.Printf("%v", output)
274+
275+
// Output: true
276+
}
277+
233278
func ExampleEval_marshal() {
234279
env := map[string]int{
235280
"foo": 1,

0 commit comments

Comments
 (0)