feat: support onEmpty optional parameter on std.minArray,std.maxArray

This commit is contained in:
John Bartholomew 2025-03-11 00:33:41 +00:00
parent 6a15a2f3fc
commit 9a89560b0d
2 changed files with 62 additions and 31 deletions

View File

@ -2280,6 +2280,7 @@ func builtinExtVar(i *interpreter, name value) (value, error) {
func builtinMinArray(i *interpreter, arguments []value) (value, error) { func builtinMinArray(i *interpreter, arguments []value) (value, error) {
arrv := arguments[0] arrv := arguments[0]
keyFv := arguments[1] keyFv := arguments[1]
onEmpty := arguments[2]
arr, err := i.getArray(arrv) arr, err := i.getArray(arrv)
if err != nil { if err != nil {
@ -2291,7 +2292,11 @@ func builtinMinArray(i *interpreter, arguments []value) (value, error) {
} }
num := arr.length() num := arr.length()
if num == 0 { if num == 0 {
if onEmpty == nil {
return nil, i.Error("Expected at least one element in array. Got none") return nil, i.Error("Expected at least one element in array. Got none")
} else {
return onEmpty, nil
}
} }
minVal, err := arr.elements[0].getValue(i) minVal, err := arr.elements[0].getValue(i)
if err != nil { if err != nil {
@ -2325,6 +2330,7 @@ func builtinMinArray(i *interpreter, arguments []value) (value, error) {
func builtinMaxArray(i *interpreter, arguments []value) (value, error) { func builtinMaxArray(i *interpreter, arguments []value) (value, error) {
arrv := arguments[0] arrv := arguments[0]
keyFv := arguments[1] keyFv := arguments[1]
onEmpty := arguments[2]
arr, err := i.getArray(arrv) arr, err := i.getArray(arrv)
if err != nil { if err != nil {
@ -2336,7 +2342,11 @@ func builtinMaxArray(i *interpreter, arguments []value) (value, error) {
} }
num := arr.length() num := arr.length()
if num == 0 { if num == 0 {
if onEmpty == nil {
return nil, i.Error("Expected at least one element in array. Got none") return nil, i.Error("Expected at least one element in array. Got none")
} else {
return onEmpty, nil
}
} }
maxVal, err := arr.elements[0].getValue(i) maxVal, err := arr.elements[0].getValue(i)
if err != nil { if err != nil {
@ -2533,7 +2543,7 @@ func flattenArgs(args callArguments, params []namedParameter, defaults []value)
} }
// Bind defaults for unsatisfied named parameters // Bind defaults for unsatisfied named parameters
for i := range params { for i := range params {
if flatArgs[i] == nil { if flatArgs[i] == nil && defaults[i] != nil {
flatArgs[i] = readyThunk(defaults[i]) flatArgs[i] = readyThunk(defaults[i])
} }
} }
@ -2551,10 +2561,14 @@ type unaryBuiltin struct {
func (b *unaryBuiltin) evalCall(args callArguments, i *interpreter) (value, error) { func (b *unaryBuiltin) evalCall(args callArguments, i *interpreter) (value, error) {
flatArgs := flattenArgs(args, b.parameters(), []value{}) flatArgs := flattenArgs(args, b.parameters(), []value{})
x, err := flatArgs[0].getValue(i) var x value
var err error
if flatArgs[0] != nil {
x, err = flatArgs[0].getValue(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
return b.function(i, x) return b.function(i, x)
} }
@ -2581,14 +2595,20 @@ type binaryBuiltin struct {
func (b *binaryBuiltin) evalCall(args callArguments, i *interpreter) (value, error) { func (b *binaryBuiltin) evalCall(args callArguments, i *interpreter) (value, error) {
flatArgs := flattenArgs(args, b.parameters(), []value{}) flatArgs := flattenArgs(args, b.parameters(), []value{})
x, err := flatArgs[0].getValue(i) var err error
var x, y value
if flatArgs[0] != nil {
x, err = flatArgs[0].getValue(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
y, err := flatArgs[1].getValue(i) }
if flatArgs[1] != nil {
y, err = flatArgs[1].getValue(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
return b.function(i, x, y) return b.function(i, x, y)
} }
@ -2615,18 +2635,26 @@ type ternaryBuiltin struct {
func (b *ternaryBuiltin) evalCall(args callArguments, i *interpreter) (value, error) { func (b *ternaryBuiltin) evalCall(args callArguments, i *interpreter) (value, error) {
flatArgs := flattenArgs(args, b.parameters(), []value{}) flatArgs := flattenArgs(args, b.parameters(), []value{})
x, err := flatArgs[0].getValue(i) var err error
var x, y, z value
if flatArgs[0] != nil {
x, err = flatArgs[0].getValue(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
y, err := flatArgs[1].getValue(i) }
if flatArgs[1] != nil {
y, err = flatArgs[1].getValue(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
z, err := flatArgs[2].getValue(i) }
if flatArgs[2] != nil {
z, err = flatArgs[2].getValue(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
return b.function(i, x, y, z) return b.function(i, x, y, z)
} }
@ -2649,6 +2677,7 @@ type generalBuiltinParameter struct {
// This spares us unnecessary evaluation. // This spares us unnecessary evaluation.
defaultValue value defaultValue value
name ast.Identifier name ast.Identifier
nonValueDefault bool
} }
// generalBuiltin covers cases that other builtin structures do not, // generalBuiltin covers cases that other builtin structures do not,
@ -2665,7 +2694,7 @@ func (b *generalBuiltin) parameters() []namedParameter {
ret := make([]namedParameter, len(b.params)) ret := make([]namedParameter, len(b.params))
for i := range ret { for i := range ret {
ret[i].name = b.params[i].name ret[i].name = b.params[i].name
if b.params[i].defaultValue != nil { if b.params[i].defaultValue != nil || b.params[i].nonValueDefault {
// This is not actually used because the defaultValue is used instead. // This is not actually used because the defaultValue is used instead.
// The only reason we don't leave it nil is because the checkArguments // The only reason we don't leave it nil is because the checkArguments
// function uses the non-nil status to indicate that the parameter // function uses the non-nil status to indicate that the parameter
@ -2693,11 +2722,13 @@ func (b *generalBuiltin) evalCall(args callArguments, i *interpreter) (value, er
values := make([]value, len(flatArgs)) values := make([]value, len(flatArgs))
for j := 0; j < len(values); j++ { for j := 0; j < len(values); j++ {
var err error var err error
if flatArgs[j] != nil {
values[j], err = flatArgs[j].getValue(i) values[j], err = flatArgs[j].getValue(i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
}
return b.function(i, values) return b.function(i, values)
} }
@ -2840,8 +2871,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{
&unaryBuiltin{name: "encodeUTF8", function: builtinEncodeUTF8, params: ast.Identifiers{"str"}}, &unaryBuiltin{name: "encodeUTF8", function: builtinEncodeUTF8, params: ast.Identifiers{"str"}},
&unaryBuiltin{name: "decodeUTF8", function: builtinDecodeUTF8, params: ast.Identifiers{"arr"}}, &unaryBuiltin{name: "decodeUTF8", function: builtinDecodeUTF8, params: ast.Identifiers{"arr"}},
&generalBuiltin{name: "sort", function: builtinSort, params: []generalBuiltinParameter{{name: "arr"}, {name: "keyF", defaultValue: functionID}}}, &generalBuiltin{name: "sort", function: builtinSort, params: []generalBuiltinParameter{{name: "arr"}, {name: "keyF", defaultValue: functionID}}},
&generalBuiltin{name: "minArray", function: builtinMinArray, params: []generalBuiltinParameter{{name: "arr"}, {name: "keyF", defaultValue: functionID}}}, &generalBuiltin{name: "minArray", function: builtinMinArray, params: []generalBuiltinParameter{{name: "arr"}, {name: "keyF", defaultValue: functionID}, {name: "onEmpty", nonValueDefault: true}}},
&generalBuiltin{name: "maxArray", function: builtinMaxArray, params: []generalBuiltinParameter{{name: "arr"}, {name: "keyF", defaultValue: functionID}}}, &generalBuiltin{name: "maxArray", function: builtinMaxArray, params: []generalBuiltinParameter{{name: "arr"}, {name: "keyF", defaultValue: functionID}, {name: "onEmpty", nonValueDefault: true}}},
&unaryBuiltin{name: "native", function: builtinNative, params: ast.Identifiers{"x"}}, &unaryBuiltin{name: "native", function: builtinNative, params: ast.Identifiers{"x"}},
&unaryBuiltin{name: "sum", function: builtinSum, params: ast.Identifiers{"arr"}}, &unaryBuiltin{name: "sum", function: builtinSum, params: ast.Identifiers{"arr"}},
&unaryBuiltin{name: "avg", function: builtinAvg, params: ast.Identifiers{"arr"}}, &unaryBuiltin{name: "avg", function: builtinAvg, params: ast.Identifiers{"arr"}},

View File

@ -150,8 +150,8 @@ func prepareStdlib(g *typeGraph) {
"sort": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF")}), "sort": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF")}),
"uniq": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF")}), "uniq": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF")}),
"sum": g.newSimpleFuncType(numberType, "arr"), "sum": g.newSimpleFuncType(numberType, "arr"),
"minArray": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF")}), "minArray": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF"), optional("onEmpty")}),
"maxArray": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF")}), "maxArray": g.newFuncType(anyArrayType, []ast.Parameter{required("arr"), optional("keyF"), optional("onEmpty")}),
"contains": g.newSimpleFuncType(boolType, "arr", "elem"), "contains": g.newSimpleFuncType(boolType, "arr", "elem"),
"avg": g.newSimpleFuncType(numberType, "arr"), "avg": g.newSimpleFuncType(numberType, "arr"),
"all": g.newSimpleFuncType(boolArrayType, "arr"), "all": g.newSimpleFuncType(boolArrayType, "arr"),