// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // operator-bool.go package expr import "fmt" //-------- NOT term func newNotTerm(tk *Token) (inst *term) { return &term{ tk: *tk, children: make([]*term, 0, 1), position: posPrefix, priority: priNot, evalFunc: evalNot, } } func evalNot(ctx ExprContext, self *term) (v any, err error) { var rightValue any if rightValue, err = self.evalPrefix(ctx); err != nil { return } if b, ok := ToBool(rightValue); ok { v = !b } else { err = self.errIncompatibleType(rightValue) } return } //-------- AND term func newAndTerm(tk *Token) (inst *term) { return &term{ tk: *tk, children: make([]*term, 0, 2), position: posInfix, priority: priAnd, evalFunc: evalAnd, } } func evalAnd(ctx ExprContext, self *term) (v any, err error) { if CtrlIsEnabled(ctx, ControlBoolShortcut) { v, err = evalAndWithShortcut(ctx, self) } else { v, err = evalAndWithoutShortcut(ctx, self) } return } func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) { var leftValue, rightValue any var leftBool, rightBool bool var lok, rok bool if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { return } leftBool, lok = ToBool(leftValue) rightBool, rok = ToBool(rightValue) if lok && rok { v = leftBool && rightBool } else { err = self.errIncompatibleTypes(leftValue, rightValue) } return } func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) { var leftValue, rightValue any if err = self.checkOperands(); err != nil { return } if leftValue, err = self.children[0].compute(ctx); err != nil { return } if leftBool, lok := ToBool(leftValue); !lok { err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", TypeName(leftValue)) return } else if !leftBool { v = false } else if rightValue, err = self.children[1].compute(ctx); err == nil { if rightBool, rok := ToBool(rightValue); rok { v = rightBool } else { err = self.errIncompatibleTypes(leftValue, rightValue) } } return } //-------- OR term func newOrTerm(tk *Token) (inst *term) { return &term{ tk: *tk, children: make([]*term, 0, 2), position: posInfix, priority: priOr, evalFunc: evalOr, } } func evalOr(ctx ExprContext, self *term) (v any, err error) { if CtrlIsEnabled(ctx, ControlBoolShortcut) { v, err = evalOrWithShortcut(ctx, self) } else { v, err = evalOrWithoutShortcut(ctx, self) } return } func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) { var leftValue, rightValue any var leftBool, rightBool bool var lok, rok bool if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { return } leftBool, lok = ToBool(leftValue) rightBool, rok = ToBool(rightValue) if lok && rok { v = leftBool || rightBool } else { err = self.errIncompatibleTypes(leftValue, rightValue) } return } func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) { var leftValue, rightValue any if err = self.checkOperands(); err != nil { return } if leftValue, err = self.children[0].compute(ctx); err != nil { return } if leftBool, lok := ToBool(leftValue); !lok { err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", TypeName(leftValue)) return } else if leftBool { v = true } else if rightValue, err = self.children[1].compute(ctx); err == nil { if rightBool, rok := ToBool(rightValue); rok { v = rightBool } else { err = self.errIncompatibleTypes(leftValue, rightValue) } } return } // init func init() { registerTermConstructor(SymNot, newNotTerm) registerTermConstructor(SymAnd, newAndTerm) registerTermConstructor(SymOr, newOrTerm) }