From 3b513164ead7d9f9f0aa55e200ebd4afad71eb17 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Tue, 12 Nov 2024 09:56:19 +1030 Subject: [PATCH] lib: add diadic min and max functions --- lib/collections.go | 64 +++++++++++++++++++++++++++++++++++++--------- testdata/max.txt | 2 ++ testdata/min.txt | 2 ++ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/lib/collections.go b/lib/collections.go index c15d8f8..bce97c9 100644 --- a/lib/collections.go +++ b/lib/collections.go @@ -178,27 +178,31 @@ import ( // // # Max // -// Returns the maximum value of a list of comparable objects: +// Returns the maximum value of a list or pair of comparable objects: // // >.max() -> // max(>) -> +// max(, ) -> // // Examples: // // [1,2,3,4,5,6,7].max() // return 7 // max([1,2,3,4,5,6,7]) // return 7 +// max(1,7) // return 7 // // # Min // -// Returns the minimum value of a list of comparable objects: +// Returns the minimum value of a list or pair of comparable objects: // // >.min() -> // min(>) -> +// min(, ) -> // // Examples: // // [1,2,3,4,5,6,7].min() // return 1 // min([1,2,3,4,5,6,7]) // return 1 +// min(1,7) // return 1 // // # Tail // @@ -384,13 +388,19 @@ func (collectionsLib) CompileOptions() []cel.EnvOption { "list_max", []*cel.Type{listV}, typeV, - cel.UnaryBinding(catch(max)), + cel.UnaryBinding(catch(maxList)), ), cel.Overload( "max_list", []*cel.Type{listV}, typeV, - cel.UnaryBinding(catch(max)), + cel.UnaryBinding(catch(maxList)), + ), + cel.Overload( + "max_pair", + []*cel.Type{typeV, typeV}, + typeV, + cel.BinaryBinding(maxDiadic), ), ), @@ -399,13 +409,19 @@ func (collectionsLib) CompileOptions() []cel.EnvOption { "list_min", []*cel.Type{listV}, typeV, - cel.UnaryBinding(catch(min)), + cel.UnaryBinding(catch(minList)), ), cel.Overload( "min_list", []*cel.Type{listV}, typeV, - cel.UnaryBinding(catch(min)), + cel.UnaryBinding(catch(minList)), + ), + cel.Overload( + "min_pair", + []*cel.Type{typeV, typeV}, + typeV, + cel.BinaryBinding(minDiadic), ), ), @@ -916,11 +932,39 @@ func collateFieldPath(arg ref.Val, path types.String) []ref.Val { return collation } -func min(arg ref.Val) ref.Val { +type comparer interface { + ref.Val + traits.Comparer +} + +func minDiadic(arg0, arg1 ref.Val) ref.Val { + return compareDiadic(arg0, arg1, -1) +} + +func maxDiadic(arg0, arg1 ref.Val) ref.Val { + return compareDiadic(arg0, arg1, 1) +} + +func compareDiadic(arg0, arg1 ref.Val, cmp types.Int) ref.Val { + a, ok := arg0.(comparer) + if !ok { + return types.NoSuchOverloadErr() + } + b, ok := arg1.(comparer) + if !ok { + return types.NoSuchOverloadErr() + } + if a.Compare(b) == cmp { + return a + } + return b +} + +func minList(arg ref.Val) ref.Val { return compare(arg, -1) } -func max(arg ref.Val) ref.Val { +func maxList(arg ref.Val) ref.Val { return compare(arg, 1) } @@ -933,10 +977,6 @@ func compare(arg ref.Val, cmp types.Int) ref.Val { return types.NewErr("no extremum of empty list") } - type comparer interface { - ref.Val - traits.Comparer - } var min comparer it := list.Iterator() for it.HasNext() == types.True { diff --git a/testdata/max.txt b/testdata/max.txt index 266a999..096c964 100644 --- a/testdata/max.txt +++ b/testdata/max.txt @@ -6,11 +6,13 @@ cmp stdout want.txt [ [1,2,3,4,5,6,7].max(), max([1,2,3,4,5,6,7]), + max(1, 2), try(max([])), ] -- want.txt -- [ 7, 7, + 2, "no extremum of empty list" ] diff --git a/testdata/min.txt b/testdata/min.txt index 96b5b1d..af33f5a 100644 --- a/testdata/min.txt +++ b/testdata/min.txt @@ -6,10 +6,12 @@ cmp stdout want.txt [ [1,2,3,4,5,6,7].min(), min([1,2,3,4,5,6,7]), + min(1, 2), try(min([])), ] -- want.txt -- [ + 1, 1, 1, "no extremum of empty list"