|
1 | 1 | # Getting Started
|
2 | 2 |
|
3 | 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 Request 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 | 4 |
|
23 |
| -#### Compile step |
24 |
| - |
25 |
| -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 or some of this. 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. |
| 5 | +## Evaluate |
26 | 6 |
|
27 | 7 | ```go
|
| 8 | +package main |
| 9 | + |
28 | 10 | import (
|
| 11 | + "fmt" |
29 | 12 | "github.com/antonmedv/expr"
|
30 |
| - "github.com/antonmedv/expr/vm" |
31 | 13 | )
|
32 | 14 |
|
33 |
| -var program *vm.Program |
| 15 | +func main() { |
| 16 | + env := map[string]interface{}{ |
| 17 | + "foo": 1, |
| 18 | + "bar": 2, |
| 19 | + } |
34 | 20 |
|
35 |
| -program, err = expr.Compile(rule, expr.Env(&Request{})) |
| 21 | + out, err := expr.Eval("foo + bar", env) |
| 22 | + |
| 23 | + if err != nil { |
| 24 | + panic(err) |
| 25 | + } |
| 26 | + fmt.Print(out) |
| 27 | +} |
36 | 28 | ```
|
37 | 29 |
|
38 |
| -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. Now users can save expressions. |
| 30 | +## Compile |
39 | 31 |
|
40 |
| -#### Example of an expression |
| 32 | +Usually we want to compile the code on save (For example, in [web user interface](https://antonmedv.github.io/expr/)). |
41 | 33 |
|
42 |
| -```coffeescript |
43 |
| -all(Ticket.Segments, {.Origin == Location}) and Date.Before(Ticket.Segments[0].Date) |
44 |
| -``` |
| 34 | +```go |
| 35 | +package main |
45 | 36 |
|
46 |
| -<p align="center">👐</p> |
| 37 | +import ( |
| 38 | + "fmt" |
| 39 | + |
| 40 | + "github.com/antonmedv/expr" |
| 41 | +) |
47 | 42 |
|
48 |
| -#### Runtime step |
| 43 | +func main() { |
| 44 | + env := map[string]interface{}{ |
| 45 | + "greet": "Hello, %v!", |
| 46 | + "names": []string{"world", "you"}, |
| 47 | + "sprintf": fmt.Sprintf, // You can pass any functions. |
| 48 | + } |
49 | 49 |
|
50 |
| -Now we need to implement the execution of our compiled program. On each request, we have some kind of filter loop. |
| 50 | + code := `sprintf(greet, names[0])` |
51 | 51 |
|
52 |
| -```go |
53 |
| -output, err := expr.Run(program, requset) |
54 |
| -if err != nil { |
55 |
| - return err |
56 |
| -} |
| 52 | + // Compile code into bytecode. This step can be done once and program may be reused. |
| 53 | + // Specify environment for type check. |
| 54 | + program, err := expr.Compile(code, expr.Env(env)) |
| 55 | + if err != nil { |
| 56 | + panic(err) |
| 57 | + } |
57 | 58 |
|
58 |
| -if !output.(bool) { |
59 |
| - continue |
| 59 | + output, err := expr.Run(program, env) |
| 60 | + if err != nil { |
| 61 | + panic(err) |
| 62 | + } |
| 63 | + |
| 64 | + fmt.Print(output) |
60 | 65 | }
|
61 | 66 | ```
|
62 | 67 |
|
63 |
| -#### Helpers |
64 |
| - |
65 |
| -Now let's some add a function for repetitive tasks. |
| 68 | +You may use existing types. For example, an environment can be a struct. |
66 | 69 |
|
67 | 70 | ```go
|
68 |
| -func (r *Request) SameLocation() bool { |
69 |
| - same := false |
70 |
| - for _, s := range r.Ticket.Segments { |
71 |
| - same = same && s.Origin == r.Location |
72 |
| - } |
73 |
| - return same |
74 |
| -} |
75 |
| -``` |
| 71 | +package main |
76 | 72 |
|
77 |
| -Now users can use functions inside expressions. |
| 73 | +import ( |
| 74 | + "fmt" |
| 75 | + "time" |
78 | 76 |
|
79 |
| -```coffeescript |
80 |
| -SameLocation() and Date.Before(Ticket.Segments[0].Date) |
81 |
| -``` |
| 77 | + "github.com/antonmedv/expr" |
| 78 | +) |
82 | 79 |
|
83 |
| -#### Operator override |
| 80 | +type Env struct { |
| 81 | + Tweets []Tweet |
| 82 | +} |
84 | 83 |
|
85 |
| -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. |
| 84 | +// Methods defined on such struct will be functions. |
| 85 | +func (Env) Format(t time.Time) string { return t.Format(time.RFC822) } |
86 | 86 |
|
87 |
| -```go |
88 |
| -func (*Request) Before(a, b time.Time) bool { |
89 |
| - return a.Before(b) |
| 87 | +type Tweet struct { |
| 88 | + Text string |
| 89 | + Date time.Time |
90 | 90 | }
|
91 |
| -``` |
92 | 91 |
|
93 |
| -Now, on compile time, override `<` operator. |
| 92 | +func main() { |
| 93 | + code := `all(Tweets, {len(.Text) > 0}) ? map(Tweets, {.Text + Format(.Date)}) : nil` |
94 | 94 |
|
95 |
| -```go |
96 |
| -program, err = expr.Compile(rule, expr.Env(&Request{}), expr.Operator("<", "Before")) |
97 |
| -``` |
| 95 | + // We can use an empty instance of the struct as an environment. |
| 96 | + |
| 97 | + program, err := expr.Compile(code, expr.Env(Env{})) |
| 98 | + if err != nil { |
| 99 | + panic(err) |
| 100 | + } |
98 | 101 |
|
99 |
| -That's it! Now users can write expressions in a more pleasant way. Other operators `+`, `>`, `==`, etc can be overridden in similar way. |
| 102 | + env := Env{ |
| 103 | + Tweets: []Tweet{{"Oh My God!", time.Now()}, {"How you doin?", time.Now()}, {"Could I be wearing any more clothes?", time.Now()}}, |
| 104 | + } |
100 | 105 |
|
101 |
| -```coffeescript |
102 |
| -SameLocation() and Date < Ticket.Segments[0].Date |
| 106 | + output, err := expr.Run(program, env) |
| 107 | + if err != nil { |
| 108 | + panic(err) |
| 109 | + } |
| 110 | + |
| 111 | + fmt.Print(output) |
| 112 | +} |
103 | 113 | ```
|
104 | 114 |
|
105 |
| -Next |
106 |
| -* [Language Definition](Language-Definition.md) |
107 |
| -* [Usage](Usage.md) |
108 |
| -* [Examples](https://godoc.org/github.com/antonmedv/expr#pkg-examples) |
| 115 | +* [Contents](README.md) |
| 116 | +* Next: [Operator Override](Operator-Override.md) |
0 commit comments