Add more optimizations

This commit is contained in:
Anton Medvedev
2019-06-25 16:25:14 +03:00
parent f718200b06
commit 02ca6fbf39
5 changed files with 95 additions and 32 deletions
-12
View File
@@ -367,18 +367,6 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
c.emit(OpEndsWith)
case "..":
min, ok1 := node.Left.(*ast.IntegerNode)
max, ok2 := node.Right.(*ast.IntegerNode)
if ok1 && ok2 {
// Create range on compile time to avoid unnecessary work at runtime.
size := max.Value - min.Value + 1
rng := make([]interface{}, size)
for i := range rng {
rng[i] = min.Value + i
}
c.emitPush(rng)
return
}
c.compile(node.Left)
c.compile(node.Right)
c.emit(OpRange)
-11
View File
@@ -130,17 +130,6 @@ func TestCompile(t *testing.T) {
},
},
},
{
`1..2`,
vm.Program{
Constants: []interface{}{
[]interface{}{1, 2},
},
Bytecode: []byte{
vm.OpPush, 0, 0,
},
},
},
}
for _, test := range tests {
+8
View File
@@ -583,6 +583,14 @@ func TestExpr(t *testing.T) {
`One in 0..1 && Two not in 0..1`,
true,
},
{
`1 + 2 + Three`,
6,
},
{
`2 ** 8`,
float64(256),
},
}
for _, tt := range tests {
+62 -2
View File
@@ -2,14 +2,52 @@ package optimizer
import (
. "github.com/antonmedv/expr/ast"
"math"
)
type fold struct{}
type inRange struct{}
type constRange struct{}
func (*fold) Enter(node *Node) {}
func (*fold) Exit(node *Node) {
switch n := (*node).(type) {
case *UnaryNode:
if n.Operator == "-" {
if i, ok := n.Node.(*IntegerNode); ok {
*node = &IntegerNode{
Value: -i.Value,
}
}
}
case *BinaryNode:
switch n.Operator {
case "+":
if a, ok := n.Left.(*IntegerNode); ok {
if b, ok := n.Right.(*IntegerNode); ok {
*node = &IntegerNode{
Value: a.Value + b.Value,
}
}
}
if a, ok := n.Left.(*StringNode); ok {
if b, ok := n.Right.(*StringNode); ok {
*node = &StringNode{
Value: a.Value + b.Value,
}
}
}
case "**":
if a, ok := n.Left.(*IntegerNode); ok {
if b, ok := n.Right.(*IntegerNode); ok {
*node = &FloatNode{
Value: math.Pow(float64(a.Value), float64(b.Value)),
}
}
}
}
case *ArrayNode:
if len(n.Nodes) > 0 {
@@ -54,8 +92,8 @@ func (*inRange) Exit(node *Node) {
case *BinaryNode:
if n.Operator == "in" || n.Operator == "not in" {
if rng, ok := n.Right.(*BinaryNode); ok && rng.Operator == ".." {
if from, ok := n.Left.(*IntegerNode); ok {
if to, ok := n.Right.(*IntegerNode); ok {
if from, ok := rng.Left.(*IntegerNode); ok {
if to, ok := rng.Right.(*IntegerNode); ok {
*node = &BinaryNode{
Operator: "and",
Left: &BinaryNode{
@@ -82,7 +120,29 @@ func (*inRange) Exit(node *Node) {
}
}
func (*constRange) Enter(node *Node) {}
func (*constRange) Exit(node *Node) {
switch n := (*node).(type) {
case *BinaryNode:
if n.Operator == ".." {
if min, ok := n.Left.(*IntegerNode); ok {
if max, ok := n.Right.(*IntegerNode); ok {
size := max.Value - min.Value + 1
value := make([]int, size)
for i := range value {
value[i] = min.Value + i
}
*node = &ConstantNode{
Value: value,
}
}
}
}
}
}
func Optimize(node *Node) {
Walk(node, &fold{})
Walk(node, &inRange{})
Walk(node, &constRange{})
}
+25 -7
View File
@@ -31,16 +31,21 @@ func TestOptimize_in_range(t *testing.T) {
optimizer.Optimize(&tree.Node)
left := &ast.IdentifierNode{
Value: "age",
}
expected := &ast.BinaryNode{
Operator: "in",
Left: &ast.IdentifierNode{
Value: "age",
},
Right: &ast.BinaryNode{
Operator: "..",
Left: &ast.IntegerNode{
Operator: "and",
Left: &ast.BinaryNode{
Operator: ">=",
Left: left,
Right: &ast.IntegerNode{
Value: 18,
},
},
Right: &ast.BinaryNode{
Operator: "<=",
Left: left,
Right: &ast.IntegerNode{
Value: 31,
},
@@ -49,3 +54,16 @@ func TestOptimize_in_range(t *testing.T) {
assert.Equal(t, litter.Sdump(expected), litter.Sdump(tree.Node))
}
func TestOptimize_const_range(t *testing.T) {
tree, err := parser.Parse(`-1..1`)
require.NoError(t, err)
optimizer.Optimize(&tree.Node)
expected := &ast.ConstantNode{
Value: []int{-1, 0, 1},
}
assert.Equal(t, litter.Sdump(expected), litter.Sdump(tree.Node))
}