Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
677808fa0e
non-functional optimizer
All checks were successful
/ test (push) Successful in 46s
2026-07-15 21:49:19 +02:00

250
core/optimizer.go Normal file
View 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
}