From 19745379d9937e84926499da2443760bcac14034 Mon Sep 17 00:00:00 2001 From: aarzilli Date: Thu, 19 Oct 2023 09:46:24 +0200 Subject: [PATCH] proc: add min and max builtins Go 1.21 added two new builtins: min and max. Add them to the expression evaluator. --- pkg/proc/eval.go | 53 ++++++++++++++++++++++++++++++++++++++ pkg/proc/variables_test.go | 7 +++++ 2 files changed, 60 insertions(+) diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go index 6e4edbd972..aba8619b51 100644 --- a/pkg/proc/eval.go +++ b/pkg/proc/eval.go @@ -1498,6 +1498,8 @@ var supportedBuiltins = map[string]func([]*Variable, []ast.Expr) (*Variable, err "complex": complexBuiltin, "imag": imagBuiltin, "real": realBuiltin, + "min": minBuiltin, + "max": maxBuiltin, } func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { @@ -1662,6 +1664,57 @@ func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { return newConstant(constant.Real(arg.Value), arg.mem), nil } +func minBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + return minmaxBuiltin("min", token.LSS, args, nodeargs) +} + +func maxBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + return minmaxBuiltin("max", token.GTR, args, nodeargs) +} + +func minmaxBuiltin(name string, op token.Token, args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + var best *Variable + + for i := range args { + if args[i].Kind == reflect.String { + args[i].loadValue(loadFullValueLongerStrings) + } else { + args[i].loadValue(loadFullValue) + } + + if args[i].Unreadable != nil { + return nil, fmt.Errorf("could not load %q: %v", exprToString(nodeargs[i]), args[i].Unreadable) + } + if args[i].FloatSpecial != 0 { + return nil, errOperationOnSpecialFloat + } + + if best == nil { + best = args[i] + continue + } + + _, err := negotiateType(op, args[i], best) + if err != nil { + return nil, err + } + + v, err := compareOp(op, args[i], best) + if err != nil { + return nil, err + } + + if v { + best = args[i] + } + } + + if best == nil { + return nil, fmt.Errorf("not enough arguments to %s", name) + } + return best, nil +} + // Evaluates expressions . where subexpr is not a package name func (scope *EvalScope) evalStructSelector(op *evalop.Select, stack *evalStack) { xv := stack.pop() diff --git a/pkg/proc/variables_test.go b/pkg/proc/variables_test.go index a1a00e9971..0d9dc98fdb 100644 --- a/pkg/proc/variables_test.go +++ b/pkg/proc/variables_test.go @@ -681,6 +681,13 @@ func getEvalExpressionTestCases() []varTest { {"real(cpx1)", false, "1", "1", "", nil}, {"imag(3i)", false, "3", "3", "", nil}, {"real(4)", false, "4", "4", "", nil}, + {"max(1, 2, 3)", false, "3", "3", "", nil}, + {`max("one", "two", "three")`, false, `"two"`, `"two"`, "", nil}, + {`min("one", "two", "three")`, false, `"one"`, `"one"`, "", nil}, + {"max(s1[0], s1[1], s1[2])", false, `"two"`, `"two"`, "string", nil}, + {"min(s1[0], s1[1], s1[2])", false, `"one"`, `"one"`, "string", nil}, + {`max(s1[0], "two", s1[2])`, false, `"two"`, `"two"`, "", nil}, + {`min(s1[0], "two", s1[2])`, false, `"one"`, `"one"`, "string", nil}, // nil {"nil", false, "nil", "nil", "", nil},