Skip to content

Commit 4f8a827

Browse files
committed
Fix optimizer for in array
1 parent e49f6dd commit 4f8a827

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

expr_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,8 @@ func TestExpr(t *testing.T) {
380380
2,
381381
},
382382
{
383-
`2 ** 4`,
384-
float64(16),
383+
`2 ** 8`,
384+
float64(256),
385385
},
386386
{
387387
`-(2-5)**3-2/(+4-3)+-2`,
@@ -407,6 +407,22 @@ func TestExpr(t *testing.T) {
407407
`Int64 in 0..1`,
408408
true,
409409
},
410+
{
411+
`1 in [1, 2, 3] && "foo" in {foo: 0, bar: 1} && "Price" in Ticket`,
412+
true,
413+
},
414+
{
415+
`1 in [1.5] || 1 not in [1]`,
416+
false,
417+
},
418+
{
419+
`One in 0..1 && Two not in 0..1`,
420+
true,
421+
},
422+
{
423+
`Int32 in [10, 20]`,
424+
false,
425+
},
410426
{
411427
`String matches "s.+"`,
412428
true,
@@ -467,14 +483,6 @@ func TestExpr(t *testing.T) {
467483
`{foo: 0, bar: 1}`,
468484
map[string]interface{}{"foo": 0, "bar": 1},
469485
},
470-
{
471-
`1 in [1, 2, 3] && "foo" in {foo: 0, bar: 1} && "Price" in Ticket`,
472-
true,
473-
},
474-
{
475-
`1 in [1.5] || 1 not in [1]`,
476-
false,
477-
},
478486
{
479487
`(true ? 0+1 : 2+3) + (false ? -1 : -2)`,
480488
-1,
@@ -579,18 +587,10 @@ func TestExpr(t *testing.T) {
579587
`Array[:] == Array`,
580588
true,
581589
},
582-
{
583-
`One in 0..1 && Two not in 0..1`,
584-
true,
585-
},
586590
{
587591
`1 + 2 + Three`,
588592
6,
589593
},
590-
{
591-
`2 ** 8`,
592-
float64(256),
593-
},
594594
}
595595

596596
for _, tt := range tests {

optimizer/optimizer.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
. "github.com/antonmedv/expr/ast"
66
"math"
7+
"reflect"
78
)
89

910
type inArray struct{}
@@ -43,6 +44,13 @@ func (*inArray) Exit(node *Node) {
4344
switch n := (*node).(type) {
4445
case *BinaryNode:
4546
if n.Operator == "in" || n.Operator == "not in" {
47+
t := n.Left.GetType()
48+
if t == nil || n.Left.GetType().Kind() != reflect.Int {
49+
// This optimization can be only performed if left side is int type,
50+
// as runtime.in func uses reflect.Map.MapIndex and keys of map must,
51+
// be same as checked value type.
52+
return
53+
}
4654
if array, ok := n.Right.(*ArrayNode); ok {
4755
if len(array.Nodes) > 0 {
4856

optimizer/optimizer_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package optimizer_test
22

33
import (
44
"github.com/antonmedv/expr/ast"
5+
"github.com/antonmedv/expr/checker"
6+
"github.com/antonmedv/expr/internal/conf"
57
"github.com/antonmedv/expr/optimizer"
68
"github.com/antonmedv/expr/parser"
79
"github.com/sanity-io/litter"
@@ -25,9 +27,14 @@ func TestOptimize_constant_folding(t *testing.T) {
2527
}
2628

2729
func TestOptimize_in_array(t *testing.T) {
30+
config := conf.New(map[string]int{"v": 0})
31+
2832
tree, err := parser.Parse(`v in [1,2,3]`)
2933
require.NoError(t, err)
3034

35+
_, err = checker.Check(tree, config)
36+
require.NoError(t, err)
37+
3138
optimizer.Optimize(&tree.Node)
3239

3340
expected := &ast.BinaryNode{

0 commit comments

Comments
 (0)