// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // operator-prod.go package expr import ( "strings" "git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/scan" ) //-------- multiply term func newMultiplyTerm(tk *scan.Token) (inst *scan.Term) { return &scan.Term{ Tk: *tk, Children: make([]*scan.Term, 0, 2), Position: scan.PosInfix, Priority: scan.PriProduct, EvalFunc: evalMultiply, } } func mulValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { if kern.IsString(leftValue) && kern.IsInteger(rightValue) { s, _ := leftValue.(string) n, _ := rightValue.(int64) v = strings.Repeat(s, int(n)) } else if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) { v = kern.NumAsFloat(leftValue) * kern.NumAsFloat(rightValue) } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) { v, err = kern.MulAnyFract(leftValue, rightValue) } else { leftInt, _ := leftValue.(int64) rightInt, _ := rightValue.(int64) v = leftInt * rightInt } } else { err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) } return } func evalMultiply(ctx kern.ExprContext, prodTerm *scan.Term) (v any, err error) { var leftValue, rightValue any if leftValue, rightValue, err = prodTerm.EvalInfix(ctx); err != nil { return } return mulValues(prodTerm, leftValue, rightValue) } //-------- divide term func newDivideTerm(tk *scan.Token) (inst *scan.Term) { return &scan.Term{ Tk: *tk, Children: make([]*scan.Term, 0, 2), Position: scan.PosInfix, Priority: scan.PriProduct, EvalFunc: evalDivide, } } func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) { d := kern.NumAsFloat(rightValue) if d == 0.0 { err = opTerm.ErrDivisionByZero() } else { v = kern.NumAsFloat(leftValue) / d } } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) { v, err = kern.DivAnyFract(leftValue, rightValue) } else { leftInt, _ := leftValue.(int64) if rightInt, _ := rightValue.(int64); rightInt == 0 { err = opTerm.ErrDivisionByZero() } else { v = leftInt / rightInt } } } else if kern.IsString(leftValue) && kern.IsString(rightValue) { source := leftValue.(string) sep := rightValue.(string) v = kern.ListFromStrings(strings.Split(source, sep)) } else if kern.IsString(leftValue) && kern.IsInteger(rightValue) { source := leftValue.(string) partSize := int(rightValue.(int64)) if partSize == 0 { err = opTerm.ErrDivisionByZero() } else { partCount := len(source) / partSize remainder := len(source) % partSize listSize := partCount if remainder > 0 { listSize++ } parts := make([]any, 0, listSize) for i := 0; i < partCount; i++ { parts = append(parts, source[i*partSize:(i+1)*partSize]) } if remainder > 0 { parts = append(parts, source[len(source)-remainder:]) } v = kern.NewList(parts) } } else { err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) } return } func evalDivide(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { var leftValue, rightValue any if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil { return } return divValues(opTerm, leftValue, rightValue) } //-------- divide as float term func newDivideAsFloatTerm(tk *scan.Token) (inst *scan.Term) { return &scan.Term{ Tk: *tk, Children: make([]*scan.Term, 0, 2), Position: scan.PosInfix, Priority: scan.PriProduct, EvalFunc: evalDivideAsFloat, } } func evalDivideAsFloat(ctx kern.ExprContext, floatDivTerm *scan.Term) (v any, err error) { var leftValue, rightValue any if leftValue, rightValue, err = floatDivTerm.EvalInfix(ctx); err != nil { return } if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) { d := kern.NumAsFloat(rightValue) if d == 0.0 { err = floatDivTerm.ErrDivisionByZero() } else { v = kern.NumAsFloat(leftValue) / d } } else { err = floatDivTerm.ErrIncompatibleTypes(leftValue, rightValue) } return } //-------- reminder term func newRemainderTerm(tk *scan.Token) (inst *scan.Term) { return &scan.Term{ Tk: *tk, Children: make([]*scan.Term, 0, 2), Position: scan.PosInfix, Priority: scan.PriProduct, EvalFunc: evalRemainder, } } func remainderValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) { rightInt, _ := rightValue.(int64) if rightInt == 0 { err = opTerm.ErrDivisionByZero() } else { leftInt, _ := leftValue.(int64) v = leftInt % rightInt } } else { err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) } return } func evalRemainder(ctx kern.ExprContext, remainderTerm *scan.Term) (v any, err error) { var leftValue, rightValue any if leftValue, rightValue, err = remainderTerm.EvalInfix(ctx); err != nil { return } return remainderValues(remainderTerm, leftValue, rightValue) } // init func init() { scan.RegisterTermConstructor(scan.SymStar, newMultiplyTerm) scan.RegisterTermConstructor(scan.SymSlash, newDivideTerm) scan.RegisterTermConstructor(scan.SymDotSlash, newDivideAsFloatTerm) scan.RegisterTermConstructor(scan.SymPercent, newRemainderTerm) }