// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // operator-bool.go package expr import ( "fmt" "git.portale-stac.it/go-pkg/expr/kern" ) //-------- 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 kern.ExprContext, opTerm *term) (v any, err error) { var rightValue any if rightValue, err = opTerm.evalPrefix(ctx); err != nil { return } if b, ok := kern.ToBool(rightValue); ok { v = !b } else { err = opTerm.errIncompatiblePrefixPostfixType(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 kern.ExprContext, self *term) (v any, err error) { if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) { v, err = evalAndWithShortcut(ctx, self) } else { v, err = evalAndWithoutShortcut(ctx, self) } return } func evalAndWithoutShortcut(ctx kern.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 = kern.ToBool(leftValue) rightBool, rok = kern.ToBool(rightValue) if lok && rok { v = leftBool && rightBool } else { err = self.errIncompatibleTypes(leftValue, rightValue) } return } func evalAndWithShortcut(ctx kern.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 := kern.ToBool(leftValue); !lok { // err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue)) // return err = self.errIncompatibleType(leftValue, "left") } else if !leftBool { v = false } else if rightValue, err = self.children[1].Compute(ctx); err == nil { if rightBool, rok := kern.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 kern.ExprContext, self *term) (v any, err error) { if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) { v, err = evalOrWithShortcut(ctx, self) } else { v, err = evalOrWithoutShortcut(ctx, self) } return } func evalOrWithoutShortcut(ctx kern.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 = kern.ToBool(leftValue) rightBool, rok = kern.ToBool(rightValue) if lok && rok { v = leftBool || rightBool } else { err = self.errIncompatibleTypes(leftValue, rightValue) } return } func evalOrWithShortcut(ctx kern.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 := kern.ToBool(leftValue); !lok { err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue)) return } else if leftBool { v = true } else if rightValue, err = self.children[1].Compute(ctx); err == nil { if rightBool, rok := kern.ToBool(rightValue); rok { v = rightBool } else { err = self.errIncompatibleTypes(leftValue, rightValue) } } return } // init func init() { registerTermConstructor(SymNot, newNotTerm) registerTermConstructor(SymAnd, newAndTerm) registerTermConstructor(SymOr, newOrTerm) }