Skip to content

Commit 0fd1cfa

Browse files
committed
Add more optimizations
1 parent f7bcbfa commit 0fd1cfa

File tree

5 files changed

+95
-32
lines changed

5 files changed

+95
-32
lines changed

compiler/compiler.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -367,18 +367,6 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
367367
c.emit(OpEndsWith)
368368

369369
case "..":
370-
min, ok1 := node.Left.(*ast.IntegerNode)
371-
max, ok2 := node.Right.(*ast.IntegerNode)
372-
if ok1 && ok2 {
373-
// Create range on compile time to avoid unnecessary work at runtime.
374-
size := max.Value - min.Value + 1
375-
rng := make([]interface{}, size)
376-
for i := range rng {
377-
rng[i] = min.Value + i
378-
}
379-
c.emitPush(rng)
380-
return
381-
}
382370
c.compile(node.Left)
383371
c.compile(node.Right)
384372
c.emit(OpRange)

compiler/compiler_test.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,6 @@ func TestCompile(t *testing.T) {
130130
},
131131
},
132132
},
133-
{
134-
`1..2`,
135-
vm.Program{
136-
Constants: []interface{}{
137-
[]interface{}{1, 2},
138-
},
139-
Bytecode: []byte{
140-
vm.OpPush, 0, 0,
141-
},
142-
},
143-
},
144133
}
145134

146135
for _, test := range tests {

expr_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,14 @@ func TestExpr(t *testing.T) {
583583
`One in 0..1 && Two not in 0..1`,
584584
true,
585585
},
586+
{
587+
`1 + 2 + Three`,
588+
6,
589+
},
590+
{
591+
`2 ** 8`,
592+
float64(256),
593+
},
586594
}
587595

588596
for _, tt := range tests {

optimizer/optimizer.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,52 @@ package optimizer
22

33
import (
44
. "github.com/antonmedv/expr/ast"
5+
"math"
56
)
67

78
type fold struct{}
89
type inRange struct{}
10+
type constRange struct{}
911

1012
func (*fold) Enter(node *Node) {}
1113
func (*fold) Exit(node *Node) {
1214
switch n := (*node).(type) {
15+
case *UnaryNode:
16+
if n.Operator == "-" {
17+
if i, ok := n.Node.(*IntegerNode); ok {
18+
*node = &IntegerNode{
19+
Value: -i.Value,
20+
}
21+
}
22+
}
23+
24+
case *BinaryNode:
25+
switch n.Operator {
26+
case "+":
27+
if a, ok := n.Left.(*IntegerNode); ok {
28+
if b, ok := n.Right.(*IntegerNode); ok {
29+
*node = &IntegerNode{
30+
Value: a.Value + b.Value,
31+
}
32+
}
33+
}
34+
if a, ok := n.Left.(*StringNode); ok {
35+
if b, ok := n.Right.(*StringNode); ok {
36+
*node = &StringNode{
37+
Value: a.Value + b.Value,
38+
}
39+
}
40+
}
41+
case "**":
42+
if a, ok := n.Left.(*IntegerNode); ok {
43+
if b, ok := n.Right.(*IntegerNode); ok {
44+
*node = &FloatNode{
45+
Value: math.Pow(float64(a.Value), float64(b.Value)),
46+
}
47+
}
48+
}
49+
}
50+
1351
case *ArrayNode:
1452
if len(n.Nodes) > 0 {
1553

@@ -54,8 +92,8 @@ func (*inRange) Exit(node *Node) {
5492
case *BinaryNode:
5593
if n.Operator == "in" || n.Operator == "not in" {
5694
if rng, ok := n.Right.(*BinaryNode); ok && rng.Operator == ".." {
57-
if from, ok := n.Left.(*IntegerNode); ok {
58-
if to, ok := n.Right.(*IntegerNode); ok {
95+
if from, ok := rng.Left.(*IntegerNode); ok {
96+
if to, ok := rng.Right.(*IntegerNode); ok {
5997
*node = &BinaryNode{
6098
Operator: "and",
6199
Left: &BinaryNode{
@@ -82,7 +120,29 @@ func (*inRange) Exit(node *Node) {
82120
}
83121
}
84122

123+
func (*constRange) Enter(node *Node) {}
124+
func (*constRange) Exit(node *Node) {
125+
switch n := (*node).(type) {
126+
case *BinaryNode:
127+
if n.Operator == ".." {
128+
if min, ok := n.Left.(*IntegerNode); ok {
129+
if max, ok := n.Right.(*IntegerNode); ok {
130+
size := max.Value - min.Value + 1
131+
value := make([]int, size)
132+
for i := range value {
133+
value[i] = min.Value + i
134+
}
135+
*node = &ConstantNode{
136+
Value: value,
137+
}
138+
}
139+
}
140+
}
141+
}
142+
}
143+
85144
func Optimize(node *Node) {
86145
Walk(node, &fold{})
87146
Walk(node, &inRange{})
147+
Walk(node, &constRange{})
88148
}

optimizer/optimizer_test.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,21 @@ func TestOptimize_in_range(t *testing.T) {
3131

3232
optimizer.Optimize(&tree.Node)
3333

34+
left := &ast.IdentifierNode{
35+
Value: "age",
36+
}
3437
expected := &ast.BinaryNode{
35-
Operator: "in",
36-
Left: &ast.IdentifierNode{
37-
Value: "age",
38-
},
39-
Right: &ast.BinaryNode{
40-
Operator: "..",
41-
Left: &ast.IntegerNode{
38+
Operator: "and",
39+
Left: &ast.BinaryNode{
40+
Operator: ">=",
41+
Left: left,
42+
Right: &ast.IntegerNode{
4243
Value: 18,
4344
},
45+
},
46+
Right: &ast.BinaryNode{
47+
Operator: "<=",
48+
Left: left,
4449
Right: &ast.IntegerNode{
4550
Value: 31,
4651
},
@@ -49,3 +54,16 @@ func TestOptimize_in_range(t *testing.T) {
4954

5055
assert.Equal(t, litter.Sdump(expected), litter.Sdump(tree.Node))
5156
}
57+
58+
func TestOptimize_const_range(t *testing.T) {
59+
tree, err := parser.Parse(`-1..1`)
60+
require.NoError(t, err)
61+
62+
optimizer.Optimize(&tree.Node)
63+
64+
expected := &ast.ConstantNode{
65+
Value: []int{-1, 0, 1},
66+
}
67+
68+
assert.Equal(t, litter.Sdump(expected), litter.Sdump(tree.Node))
69+
}

0 commit comments

Comments
 (0)