Skip to content

Commit 6d40552

Browse files
committed
Refactor operator patcher
1 parent 0107cca commit 6d40552

File tree

4 files changed

+51
-58
lines changed

4 files changed

+51
-58
lines changed

compiler/patcher.go

Lines changed: 0 additions & 44 deletions
This file was deleted.

conf/operators_table.go renamed to conf/operators.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package conf
22

3-
import "reflect"
3+
import (
4+
"github.com/antonmedv/expr/ast"
5+
"reflect"
6+
)
47

58
// OperatorsTable maps binary operators to corresponding list of functions.
69
// Functions should be provided in the environment to allow operator overloading.
@@ -24,3 +27,33 @@ func FindSuitableOperatorOverload(fns []string, types TypesTable, l, r reflect.T
2427
}
2528
return nil, "", false
2629
}
30+
31+
type OperatorPatcher struct {
32+
Operators OperatorsTable
33+
Types TypesTable
34+
}
35+
36+
func (p *OperatorPatcher) Enter(_ *ast.Node) {}
37+
func (p *OperatorPatcher) Exit(node *ast.Node) {
38+
binaryNode, ok := (*node).(*ast.BinaryNode)
39+
if !ok {
40+
return
41+
}
42+
43+
fns, ok := p.Operators[binaryNode.Operator]
44+
if !ok {
45+
return
46+
}
47+
48+
leftType := binaryNode.Left.Type()
49+
rightType := binaryNode.Right.Type()
50+
51+
_, fn, ok := FindSuitableOperatorOverload(fns, p.Types, leftType, rightType)
52+
if ok {
53+
newNode := &ast.CallNode{
54+
Callee: &ast.IdentifierNode{Value: fn},
55+
Arguments: []ast.Node{binaryNode.Left, binaryNode.Right},
56+
}
57+
ast.Patch(node, newNode)
58+
}
59+
}

expr.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
133133
op(config)
134134
}
135135

136+
if len(config.Operators) > 0 {
137+
config.Visitors = append(config.Visitors, &conf.OperatorPatcher{
138+
Operators: config.Operators,
139+
Types: config.Types,
140+
})
141+
}
142+
136143
if err := config.Check(); err != nil {
137144
return nil, err
138145
}
@@ -142,25 +149,22 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
142149
return nil, err
143150
}
144151

145-
_, err = checker.Check(tree, config)
146-
147-
// If we have a patch to apply, it may fix out error and
148-
// second type check is needed. Otherwise, it is an error.
149-
if err != nil && len(config.Visitors) == 0 {
150-
return nil, err
151-
}
152-
153-
// Patch operators before Optimize, as we may also mark it as ConstExpr.
154-
compiler.PatchOperators(&tree.Node, config)
155-
156-
if len(config.Visitors) >= 0 {
152+
if len(config.Visitors) > 0 {
157153
for _, v := range config.Visitors {
154+
// We need to perform types check, because some visitors may rely on
155+
// types information available in the tree.
156+
_, _ = checker.Check(tree, config)
158157
ast.Walk(&tree.Node, v)
159158
}
160159
_, err = checker.Check(tree, config)
161160
if err != nil {
162161
return nil, err
163162
}
163+
} else {
164+
_, err = checker.Check(tree, config)
165+
if err != nil {
166+
return nil, err
167+
}
164168
}
165169

166170
if config.Optimize {

expr_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ func TestCompile_exposed_error(t *testing.T) {
12771277
require.Equal(t, `{"Line":1,"Column":2,"Message":"invalid operation: == (mismatched types int and bool)","Snippet":"\n | 1 == true\n | ..^"}`, string(b))
12781278
}
12791279

1280-
func TestAsBool_exposed_error_(t *testing.T) {
1280+
func TestAsBool_exposed_error(t *testing.T) {
12811281
_, err := expr.Compile(`42`, expr.AsBool())
12821282
require.Error(t, err)
12831283

0 commit comments

Comments
 (0)