mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Add more optimizations
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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{})
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user