This commit is contained in:
parent
9e90bdfc33
commit
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