Skip to content

Commit 5724806

Browse files
committed
Update patcher documentation
1 parent dfad27f commit 5724806

File tree

1 file changed

+23
-70
lines changed

1 file changed

+23
-70
lines changed

docs/Visitor-and-Patch.md

Lines changed: 23 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,31 @@ Specify a visitor to modify the AST with `expr.Patch` function.
4747
program, err := expr.Compile(code, expr.Patch(&visitor{}))
4848
```
4949

50-
For example, we are going to replace the expression `list[-1]` with the `list[len(list)-1]`.
50+
For example, let's pass a context to every function call:
5151

5252
```go
53+
package main
54+
55+
import (
56+
"context"
57+
"fmt"
58+
"reflect"
59+
60+
"github.com/antonmedv/expr"
61+
"github.com/antonmedv/expr/ast"
62+
)
63+
5364
func main() {
5465
env := map[string]interface{}{
55-
"list": []int{1, 2, 3},
66+
"foo": func(ctx context.Context, arg int) any {
67+
// ...
68+
},
69+
"ctx": context.Background(),
5670
}
5771

58-
code := `list[-1]` // will output 3
72+
code := `foo(42)` // will be converted to foo(ctx, 42)
5973

60-
program, err := expr.Compile(code, expr.Env(env), expr.Patch(&patcher{}))
74+
program, err := expr.Compile(code, expr.Env(env), expr.Patch(patcher{}))
6175
if err != nil {
6276
panic(err)
6377
}
@@ -71,75 +85,14 @@ func main() {
7185

7286
type patcher struct{}
7387

74-
func (p *patcher) Visit(node *ast.Node) {
75-
n, ok := (*node).(*ast.IndexNode)
76-
if !ok {
77-
return
78-
}
79-
unary, ok := n.Index.(*ast.UnaryNode)
88+
var contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
89+
90+
func (patcher) Visit(node *ast.Node) {
91+
callNode, ok := (*node).(*ast.CallNode)
8092
if !ok {
8193
return
8294
}
83-
if unary.Operator == "-" {
84-
ast.Patch(&n.Index, &ast.BinaryNode{
85-
Operator: "-",
86-
Left: &ast.BuiltinNode{Name: "len", Arguments: []ast.Node{n.Node}},
87-
Right: unary.Node,
88-
})
89-
}
90-
95+
callNode.Arguments = append([]ast.Node{&ast.IdentifierNode{Value: "ctx"}}, callNode.Arguments...)
9196
}
92-
```
93-
94-
Type information is also available. In the following example, any struct
95-
implementing the `fmt.Stringer` interface is automatically converted to `string` type.
9697

97-
```go
98-
func main() {
99-
code := `Price == "$100"`
100-
101-
program, err := expr.Compile(code, expr.Env(Env{}), expr.Patch(&stringerPatcher{}))
102-
if err != nil {
103-
panic(err)
104-
}
105-
106-
env := Env{100_00}
107-
108-
output, err := expr.Run(program, env)
109-
if err != nil {
110-
panic(err)
111-
}
112-
fmt.Print(output)
113-
}
114-
115-
type Env struct {
116-
Price Price
117-
}
118-
119-
type Price int
120-
121-
func (p Price) String() string {
122-
return fmt.Sprintf("$%v", int(p)/100)
123-
}
124-
125-
var stringer = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
126-
127-
type stringerPatcher struct{}
128-
129-
func (p *stringerPatcher) Visit(node *ast.Node) {
130-
t := (*node).Type()
131-
if t == nil {
132-
return
133-
}
134-
if t.Implements(stringer) {
135-
ast.Patch(node, &ast.CallNode{
136-
Callee: &ast.MemberNode{
137-
Node: *node,
138-
Field: "String",
139-
Property: &ast.StringNode{Value: "String"},
140-
},
141-
})
142-
}
143-
144-
}
14598
```

0 commit comments

Comments
 (0)