expr/term.go

297 lines
6.4 KiB
Go

// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// term.go
package expr
import (
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
type termPriority uint32
const (
priNone termPriority = iota
priRange
priIterOp // map, filter, digest, etc
priBut
priAssign
priInsert
priOr
priAnd
priNot
priRelational
priBitwiseOr
priBitwiseAnd
priBitwiseNot
priSum
priProduct
priFraction
priSelector
priBinShift
priSign
priFact
priIterValue
priDefault
priIncDec
priDot
priDereference
priValue
)
type termPosition uint8
const (
posLeaf termPosition = iota
posInfix
posPrefix
posPostfix
posMultifix
)
type evalFuncType func(ctx kern.ExprContext, self *term) (v any, err error)
type term struct {
tk Token
parent *term
children []*term
position termPosition // operator position: leaf, infix, prefix, postfix, multifix
priority termPriority // operator priority: higher value means higher priority
evalFunc evalFuncType
}
func (t *term) Clone() (d *term) {
var children []*term
if t.children != nil {
children = make([]*term, len(t.children))
for i, c := range t.children {
children[i] = c.Clone()
}
}
d = &term{
tk: *t.tk.Clone(),
parent: t.parent,
children: children,
position: t.position,
priority: t.priority,
evalFunc: t.evalFunc,
}
return
}
func (t *term) String() string {
var sb strings.Builder
t.toString(&sb)
return sb.String()
}
func (t *term) toString(sb *strings.Builder) {
if t.position == posLeaf {
sb.WriteString(t.tk.String())
} else {
sb.WriteByte('[')
sb.WriteString(t.tk.String())
if t.children != nil {
sb.WriteByte('(')
for i, c := range t.children {
if i > 0 {
sb.WriteByte(' ')
}
c.toString(sb)
}
sb.WriteByte(')')
}
sb.WriteByte(']')
}
}
func (t *term) GetChildCount() (count int) {
if t.position == posLeaf || t.children == nil {
count = 0
} else {
count = len(t.children)
}
return
}
func (t *term) getRoom() (room int) {
switch t.position {
case posLeaf:
room = 0
case posInfix:
room = 2
case posPostfix, posPrefix:
room = 1
default:
panic("Invalid node position")
}
return
}
func (t *term) isComplete() bool {
return t.GetChildCount() == t.getRoom()
}
func (t *term) removeLastChild() (child *term) {
if t.children != nil {
child = t.children[len(t.children)-1]
t.children = t.children[0 : len(t.children)-1]
} else {
panic("Can't get last child")
}
return
}
func (t *term) isLeaf() bool {
return t.position == posLeaf
}
func (t *term) getPriority() termPriority {
return t.priority
}
func (t *term) setParent(parent *term) {
t.parent = parent
if parent != nil && len(parent.children) < cap(parent.children) {
parent.children = append(parent.children, t)
}
}
func (t *term) symbol() Symbol {
return t.tk.Sym
}
func (t *term) Source() string {
return t.tk.source
}
func (t *term) value() any {
return t.tk.Value
}
func (t *term) GetChild(index int) (c kern.Term) {
if index >= 0 && index < len(t.children) {
c = t.children[index]
}
return
}
func (t *term) GetChildSource(index int) (s string) {
if index >= 0 && index < len(t.children) {
s = t.children[index].tk.source
}
return
}
func (t *term) IsAssign() bool {
return t.symbol() == SymEqual
}
func (t *term) Compute(ctx kern.ExprContext) (v any, err error) {
if t.evalFunc == nil {
err = t.Errorf("undefined eval-func for %q term", t.Source())
} else {
v, err = t.evalFunc(ctx, t)
}
return
}
// func (term *term) toInt(computedValue any, valueDescription string) (i int, err error) {
// if index64, ok := computedValue.(int64); ok {
// i = int(index64)
// } else {
// err = term.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
// }
// return
// }
func (t *term) errIncompatibleTypes(leftValue, rightValue any) error {
leftType := kern.TypeName(leftValue)
leftText := kern.GetFormatted(leftValue, kern.Truncate)
rightType := kern.TypeName(rightValue)
rightText := kern.GetFormatted(rightValue, kern.Truncate)
return t.tk.Errorf(
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
leftText, leftType,
rightText, rightType,
t.Source())
}
func (t *term) errIncompatibleType(value any, side string) error {
return t.tk.Errorf(
"operator %q does not support operand '%v' [%s] on its %s side",
t.Source(), value, kern.TypeName(value), side)
}
func (t *term) errIncompatiblePrefixPostfixType(value any) error {
return t.tk.Errorf(
"prefix/postfix operator %q does not support operand '%v' [%s]",
t.Source(), value, kern.TypeName(value))
}
func (t *term) errDivisionByZero() error {
return t.tk.Errorf("division by zero")
}
func (t *term) Errorf(template string, args ...any) (err error) {
err = t.tk.Errorf(template, args...)
return
}
func (t *term) checkOperands() (err error) {
switch t.position {
case posInfix:
if t.children == nil || len(t.children) != 2 || t.anyChildrenNil() {
err = t.tk.Errorf("infix operator %q requires two non-nil operands, got %d", t.Source(), t.GetChildCount())
}
case posPrefix:
if t.children == nil || len(t.children) != 1 || t.children[0] == nil {
err = t.tk.Errorf("prefix operator %q requires one non-nil operand", t.tk.String())
}
case posPostfix:
if t.children == nil || len(t.children) != 1 || t.anyChildrenNil() {
err = t.tk.Errorf("postfix operator %q requires one non-nil operand", t.tk.String())
}
case posMultifix:
if t.children == nil || len(t.children) < 3 || t.anyChildrenNil() {
err = t.tk.Errorf("infix operator %q requires at least three not operands, got %d", t.Source(), t.GetChildCount())
}
}
return
}
func (t *term) anyChildrenNil() bool {
for _, child := range t.children {
if child == nil {
return true
}
}
return false
}
func (t *term) evalInfix(ctx kern.ExprContext) (leftValue, rightValue any, err error) {
if err = t.checkOperands(); err == nil {
if leftValue, err = t.children[0].Compute(ctx); err == nil {
rightValue, err = t.children[1].Compute(ctx)
}
}
return
}
func (t *term) evalPrefix(ctx kern.ExprContext) (childValue any, err error) {
if err = t.checkOperands(); err == nil {
childValue, err = t.children[0].Compute(ctx)
}
return
}
// NOTE Temporary solution to support function parameters with default value
func (t *term) forceChild(c *term) {
if t.children == nil {
t.children = make([]*term, 0, 1)
}
t.children = append(t.children, c)
}