227 lines
5.0 KiB
Go
227 lines
5.0 KiB
Go
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserved.
|
|
|
|
// term.go
|
|
package expr
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
type termPriority uint32
|
|
|
|
const (
|
|
priNone termPriority = iota
|
|
priBut
|
|
priAssign
|
|
priOr
|
|
priAnd
|
|
priNot
|
|
priRelational
|
|
priSum
|
|
priProduct
|
|
priFraction
|
|
priSelector
|
|
priSign
|
|
priFact
|
|
priIterValue
|
|
priCoalesce
|
|
priPrePost
|
|
priDot
|
|
priValue
|
|
)
|
|
|
|
type termPosition uint8
|
|
|
|
const (
|
|
posLeaf termPosition = iota
|
|
posInfix
|
|
posPrefix
|
|
posPostfix
|
|
posMultifix
|
|
)
|
|
|
|
type evalFuncType func(ctx 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 (self *term) String() string {
|
|
var sb strings.Builder
|
|
self.toString(&sb)
|
|
return sb.String()
|
|
}
|
|
|
|
func (self *term) toString(sb *strings.Builder) {
|
|
if self.position == posLeaf {
|
|
sb.WriteString(self.tk.String())
|
|
} else {
|
|
sb.WriteByte('[')
|
|
sb.WriteString(self.tk.String())
|
|
if self.children != nil {
|
|
sb.WriteByte('(')
|
|
for i, c := range self.children {
|
|
if i > 0 {
|
|
sb.WriteByte(' ')
|
|
}
|
|
c.toString(sb)
|
|
}
|
|
sb.WriteByte(')')
|
|
}
|
|
sb.WriteByte(']')
|
|
}
|
|
}
|
|
|
|
func (self *term) getChildrenCount() (count int) {
|
|
if self.position == posLeaf || self.children == nil {
|
|
count = 0
|
|
} else {
|
|
count = len(self.children)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *term) getRoom() (room int) {
|
|
switch self.position {
|
|
case posLeaf:
|
|
room = 0
|
|
case posInfix:
|
|
room = 2
|
|
case posPostfix, posPrefix:
|
|
room = 1
|
|
default:
|
|
panic("Invalid node position")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *term) isComplete() bool {
|
|
return self.getChildrenCount() == self.getRoom()
|
|
}
|
|
|
|
func (self *term) removeLastChild() (child *term) {
|
|
if self.children != nil {
|
|
child = self.children[len(self.children)-1]
|
|
self.children = self.children[0 : len(self.children)-1]
|
|
} else {
|
|
panic("Can't get last child")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *term) isLeaf() bool {
|
|
return self.position == posLeaf
|
|
}
|
|
|
|
func (self *term) getPriority() termPriority {
|
|
return self.priority
|
|
}
|
|
|
|
func (self *term) setParent(parent *term) {
|
|
self.parent = parent
|
|
if parent != nil && len(parent.children) < cap(parent.children) {
|
|
parent.children = append(parent.children, self)
|
|
}
|
|
}
|
|
|
|
func (self *term) symbol() Symbol {
|
|
return self.tk.Sym
|
|
}
|
|
|
|
func (self *term) source() string {
|
|
return self.tk.source
|
|
}
|
|
|
|
func (self *term) value() any {
|
|
return self.tk.Value
|
|
}
|
|
|
|
func (self *term) compute(ctx ExprContext) (v any, err error) {
|
|
if self.evalFunc == nil {
|
|
err = self.tk.Errorf("undefined eval-func for %q term", self.source())
|
|
} else {
|
|
v, err = self.evalFunc(ctx, self)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *term) toInt(computedValue any, valueDescription string) (i int, err error) {
|
|
if index64, ok := computedValue.(int64); ok {
|
|
i = int(index64)
|
|
} else {
|
|
err = self.Errorf("%s, got %T (%v)", valueDescription, computedValue, computedValue)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
|
return self.tk.Errorf(
|
|
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q",
|
|
leftValue, leftValue,
|
|
rightValue, rightValue,
|
|
self.source())
|
|
}
|
|
|
|
func (self *term) errIncompatibleType(value any) error {
|
|
return self.tk.Errorf(
|
|
"prefix/postfix operator %q do not support operand '%v' [%T]",
|
|
self.source(), value, value)
|
|
}
|
|
|
|
func (self *term) Errorf(template string, args ...any) (err error) {
|
|
err = self.tk.Errorf(template, args...)
|
|
return
|
|
}
|
|
|
|
func (self *term) checkOperands() (err error) {
|
|
switch self.position {
|
|
case posInfix:
|
|
if self.children == nil || len(self.children) != 2 || self.anyChildrenNil() {
|
|
err = self.tk.Errorf("infix operator %q requires two non-nil operands, got %d", self.source(), self.getChildrenCount())
|
|
}
|
|
case posPrefix:
|
|
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
|
|
err = self.tk.Errorf("prefix operator %q requires one not nil operand", self.tk.String())
|
|
}
|
|
case posPostfix:
|
|
if self.children == nil || len(self.children) != 1 || self.anyChildrenNil() {
|
|
err = self.tk.Errorf("postfix operator %q requires one not nil operand", self.tk.String())
|
|
}
|
|
case posMultifix:
|
|
if self.children == nil || len(self.children) < 3 || self.anyChildrenNil() {
|
|
err = self.tk.Errorf("infix operator %q requires at least three not operands, got %d", self.source(), self.getChildrenCount())
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *term) anyChildrenNil() bool {
|
|
for _, child := range self.children {
|
|
if child == nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
func (self *term) evalInfix(ctx ExprContext) (leftValue, rightValue any, err error) {
|
|
if err = self.checkOperands(); err == nil {
|
|
if leftValue, err = self.children[0].compute(ctx); err == nil {
|
|
rightValue, err = self.children[1].compute(ctx)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *term) evalPrefix(ctx ExprContext) (childValue any, err error) {
|
|
if err = self.checkOperands(); err == nil {
|
|
childValue, err = self.children[0].compute(ctx)
|
|
}
|
|
return
|
|
}
|