Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 677808fa0e |
1 changed files with 250 additions and 0 deletions
250
core/optimizer.go
Normal file
250
core/optimizer.go
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TreeOptimizer struct{}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) Optimize(node *Node) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) error(message string, causer Bounded) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTreeConstant check if a node tree is constant (predictable)
|
||||||
|
func (t *TreeOptimizer) isTreeConstant(tree Node) bool {
|
||||||
|
switch tree.Type() {
|
||||||
|
case StringNodeType, FloatNodeType, IntegerNodeType, BooleanNodeType, NilNodeType:
|
||||||
|
return true
|
||||||
|
case ListNodeType:
|
||||||
|
for _, item := range tree.(*ListNode).items {
|
||||||
|
if !t.isTreeConstant(item) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
case UnaryNodeType:
|
||||||
|
return t.isTreeConstant(tree.(*UnaryNode).value)
|
||||||
|
case BinaryNodeType:
|
||||||
|
return t.isTreeConstant(tree.(*BinaryNode).Left) && t.isTreeConstant(tree.(*BinaryNode).Right)
|
||||||
|
case InvokeNodeType:
|
||||||
|
for _, arg := range tree.(*InvokeNode).args {
|
||||||
|
if !t.isTreeConstant(arg) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.isTreeConstant(tree.(*InvokeNode).source)
|
||||||
|
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType,
|
||||||
|
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected node %s", tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) compute(tree Node) (Value, error) {
|
||||||
|
switch n := tree.(type) {
|
||||||
|
case *StringNode:
|
||||||
|
return &StringValue{
|
||||||
|
EscapeString(n.value),
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *FloatNode:
|
||||||
|
return &FloatValue{
|
||||||
|
n.value,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *IntegerNode:
|
||||||
|
return &IntegerValue{
|
||||||
|
n.value,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *BooleanNode:
|
||||||
|
return &BoolValue{
|
||||||
|
n.Boolean,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *NilNode:
|
||||||
|
return &NilValue{}, nil
|
||||||
|
|
||||||
|
case *ListNode:
|
||||||
|
items := make([]Value, len(n.items))
|
||||||
|
var err error
|
||||||
|
for i, item := range n.items {
|
||||||
|
items[i], err = t.compute(item)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ListValue{
|
||||||
|
items,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *BinaryNode:
|
||||||
|
return t.computeBinary(n)
|
||||||
|
|
||||||
|
case *UnaryNode:
|
||||||
|
v, err := t.compute(n.value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n.UnaryOperation {
|
||||||
|
case UnaryNegate:
|
||||||
|
if v.Type() == FloatValueType {
|
||||||
|
return &FloatValue{
|
||||||
|
-v.(*FloatValue).Number,
|
||||||
|
}, nil
|
||||||
|
} else if v.Type() == IntegerValueType {
|
||||||
|
return &IntegerValue{
|
||||||
|
new(big.Int).Neg(v.(*IntegerValue).Number),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n)
|
||||||
|
case UnaryNot:
|
||||||
|
if v.Type() != BoolValueType {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot invert %s value (not a boolean)", v.Type()), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BoolValue{
|
||||||
|
!v.(*BoolValue).Boolean,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, t.error(fmt.Sprintf("unimplemented unary %s", v.Type()), n)
|
||||||
|
|
||||||
|
case *InvokeNode:
|
||||||
|
source, err := t.compute(n.source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, ok := source.(*BuiltinFunctionValue)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.Constant {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]Value, len(f.Signature.In))
|
||||||
|
for i, arg := range n.args {
|
||||||
|
args[i], err = t.compute(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.F(nil, nil, args)
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected node %s, %T", tree.String(), tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) computeBinary(n *BinaryNode) (Value, error) {
|
||||||
|
l, err := t.compute(n.Left)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r, err := t.compute(n.Right)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Type() != r.Type() {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot %s different types %s and %s", n.BinaryOperation, l.Type(), r.Type()), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform type check
|
||||||
|
switch n.BinaryOperation {
|
||||||
|
case BinarySubtraction, BinaryMultiplication, BinaryDivision, BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
||||||
|
if l.Type() != FloatValueType && l.Type() != IntegerValueType {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinaryBooleanAnd, BinaryBooleanOr:
|
||||||
|
if l.Type() != BoolValueType {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot %s values of non-boolean type %s", n.BinaryOperation, l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinaryEquality, BinaryInequality:
|
||||||
|
// can compare all types with themselves
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var v interface{}
|
||||||
|
switch n.BinaryOperation {
|
||||||
|
case BinaryAddition:
|
||||||
|
switch l.Type() {
|
||||||
|
case FloatValueType:
|
||||||
|
v = l.(*FloatValue).Number + r.(*FloatValue).Number
|
||||||
|
case StringValueType:
|
||||||
|
v = l.(*StringValue).Text + r.(*StringValue).Text
|
||||||
|
case ListValueType:
|
||||||
|
v = append(l.(*ListValue).Items, r.(*ListValue).Items...)
|
||||||
|
case IntegerValueType:
|
||||||
|
v = new(big.Int).Add(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
default:
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot add values of type %s", l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinarySubtraction:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number - r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = new(big.Int).Sub(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
}
|
||||||
|
case BinaryMultiplication:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number * r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = new(big.Int).Mul(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
}
|
||||||
|
case BinaryDivision:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number / r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = new(big.Int).Div(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
}
|
||||||
|
case BinaryBooleanAnd:
|
||||||
|
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
|
||||||
|
case BinaryBooleanOr:
|
||||||
|
v = l.(*BoolValue).Boolean || r.(*BoolValue).Boolean
|
||||||
|
case BinaryEquality:
|
||||||
|
v = l.Equals(r)
|
||||||
|
case BinaryInequality:
|
||||||
|
v = !l.Equals(r)
|
||||||
|
case BinaryLess:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number < r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == -1
|
||||||
|
}
|
||||||
|
case BinaryGreater:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number > r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == 1
|
||||||
|
}
|
||||||
|
case BinaryLessEqual:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number <= r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
|
||||||
|
}
|
||||||
|
case BinaryGreaterEqual:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number >= r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GoToValue(v), nil
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue