Skip to content

Commit 6723e69

Browse files
committed
Update docs
1 parent 30777b6 commit 6723e69

File tree

6 files changed

+268
-312
lines changed

6 files changed

+268
-312
lines changed

docs/Getting-Started.md

Lines changed: 79 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,116 @@
11
# Getting Started
22

33
**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-
```
224

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
266

277
```go
8+
package main
9+
2810
import (
11+
"fmt"
2912
"github.com/antonmedv/expr"
30-
"github.com/antonmedv/expr/vm"
3113
)
3214

33-
var program *vm.Program
15+
func main() {
16+
env := map[string]interface{}{
17+
"foo": 1,
18+
"bar": 2,
19+
}
3420

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+
}
3628
```
3729

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
3931

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/)).
4133

42-
```coffeescript
43-
all(Ticket.Segments, {.Origin == Location}) and Date.Before(Ticket.Segments[0].Date)
44-
```
34+
```go
35+
package main
4536

46-
<p align="center">👐</p>
37+
import (
38+
"fmt"
39+
40+
"github.com/antonmedv/expr"
41+
)
4742

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+
}
4949

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])`
5151

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+
}
5758

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)
6065
}
6166
```
6267

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.
6669

6770
```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
7672

77-
Now users can use functions inside expressions.
73+
import (
74+
"fmt"
75+
"time"
7876

79-
```coffeescript
80-
SameLocation() and Date.Before(Ticket.Segments[0].Date)
81-
```
77+
"github.com/antonmedv/expr"
78+
)
8279

83-
#### Operator override
80+
type Env struct {
81+
Tweets []Tweet
82+
}
8483

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) }
8686

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
9090
}
91-
```
9291

93-
Now, on compile time, override `<` operator.
92+
func main() {
93+
code := `all(Tweets, {len(.Text) > 0}) ? map(Tweets, {.Text + Format(.Date)}) : nil`
9494

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+
}
98101

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+
}
100105

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+
}
103113
```
104114

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)

docs/Operator-Override.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Operator Override
2+
3+
**Expr** supports operator overriding. For example, you may rewrite such expression:
4+
5+
```js
6+
Now().Sub(CreatedAt)
7+
```
8+
9+
To use `-` operator:
10+
11+
```js
12+
Now() - CreatedAt
13+
```
14+
15+
To override operator use [expr.Operator](https://pkg.go.dev/github.com/antonmedv/expr?tab=doc#Operator):
16+
17+
```go
18+
package main
19+
20+
import (
21+
"fmt"
22+
"time"
23+
24+
"github.com/antonmedv/expr"
25+
)
26+
27+
func main() {
28+
code := `(Now() - CreatedAt).Hours() / 24 / 365`
29+
30+
// We can define options before compiling.
31+
options := []expr.Option{
32+
expr.Env(Env{}),
33+
expr.Operator("-", "Sub"), // Override `-` with function `Sub`.
34+
}
35+
36+
program, err := expr.Compile(code, options...)
37+
if err != nil {
38+
panic(err)
39+
}
40+
41+
env := Env{
42+
CreatedAt: time.Date(1987, time.November, 24, 20, 0, 0, 0, time.UTC),
43+
}
44+
45+
output, err := expr.Run(program, env)
46+
if err != nil {
47+
panic(err)
48+
}
49+
fmt.Print(output)
50+
}
51+
52+
type Env struct {
53+
datetime
54+
CreatedAt time.Time
55+
}
56+
57+
// Functions may be defined on embedded structs as well.
58+
type datetime struct{}
59+
60+
func (datetime) Now() time.Time { return time.Now() }
61+
func (datetime) Sub(a, b time.Time) time.Duration { return a.Sub(b) }
62+
```
63+
64+
**Expr** uses defined functions on `Env` for operator overloading. If types of operands match types of a function,
65+
the operator will be replaced with the function call.
66+
67+
Complete example can be found here: [dates_test.go](examples/dates_test.go).
68+
69+
* [Contents](README.md)
70+
* Next: [Visitor And Patch](Visitor-And-Patch.md)

docs/Optimizations.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,7 @@ fib(42)
7676
```
7777

7878
Will be replaced with result of `fib(42)` on compile step. No need to calculate it during runtime.
79+
80+
[ConstExpr Example](https://pkg.go.dev/github.com/antonmedv/expr?tab=doc#ConstExpr)
81+
82+
* [Contents](README.md)

docs/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
* [Getting Started](Getting-Started.md)
44
* [Language Definition](Language-Definition.md)
5-
* [Usage](Usage.md)
5+
* [Operator Override](Operator-Override.md)
6+
* [Visitor And Patch](Visitor-And-Patch.md)
67
* [Optimizations](Optimizations.md)

0 commit comments

Comments
 (0)