|
| 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) |
0 commit comments