Skip to content

Commit 0ef7dc5

Browse files
committed
Add predicate to sum() builtin
1 parent 4cdfd38 commit 0ef7dc5

File tree

10 files changed

+107
-65
lines changed

10 files changed

+107
-65
lines changed

builtin/builtin.go

+5-7
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ var Builtins = []*Function{
8383
Predicate: true,
8484
Types: types(new(func([]any, func(any) bool) int)),
8585
},
86+
{
87+
Name: "sum",
88+
Predicate: true,
89+
Types: types(new(func([]any, func(any) bool) int)),
90+
},
8691
{
8792
Name: "groupBy",
8893
Predicate: true,
@@ -387,13 +392,6 @@ var Builtins = []*Function{
387392
return validateAggregateFunc("min", args)
388393
},
389394
},
390-
{
391-
Name: "sum",
392-
Func: sum,
393-
Validate: func(args []reflect.Type) (reflect.Type, error) {
394-
return validateAggregateFunc("sum", args)
395-
},
396-
},
397395
{
398396
Name: "mean",
399397
Func: func(args ...any) (any, error) {

builtin/builtin_test.go

-4
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,6 @@ func TestBuiltin(t *testing.T) {
9090
{`sum([.5, 1.5, 2.5])`, 4.5},
9191
{`sum([])`, 0},
9292
{`sum([1, 2, 3.0, 4])`, 10.0},
93-
{`sum(10, [1, 2, 3], 1..9)`, 61},
94-
{`sum(-10, [1, 2, 3, 4])`, 0},
95-
{`sum(-10.9, [1, 2, 3, 4, 9])`, 8.1},
9693
{`mean(1..9)`, 5.0},
9794
{`mean([.5, 1.5, 2.5])`, 1.5},
9895
{`mean([])`, 0.0},
@@ -219,7 +216,6 @@ func TestBuiltin_errors(t *testing.T) {
219216
{`min([1, "2"])`, `invalid argument for min (type string)`},
220217
{`median(1..9, "t")`, "invalid argument for median (type string)"},
221218
{`mean("s", 1..9)`, "invalid argument for mean (type string)"},
222-
{`sum("s", "h")`, "invalid argument for sum (type string)"},
223219
{`duration("error")`, `invalid duration`},
224220
{`date("error")`, `invalid date`},
225221
{`get()`, `invalid number of arguments (expected 2, got 0)`},

builtin/lib.go

-39
Original file line numberDiff line numberDiff line change
@@ -254,45 +254,6 @@ func String(arg any) any {
254254
return fmt.Sprintf("%v", arg)
255255
}
256256

257-
func sum(args ...any) (any, error) {
258-
var total int
259-
var fTotal float64
260-
261-
for _, arg := range args {
262-
rv := reflect.ValueOf(deref.Deref(arg))
263-
264-
switch rv.Kind() {
265-
case reflect.Array, reflect.Slice:
266-
size := rv.Len()
267-
for i := 0; i < size; i++ {
268-
elemSum, err := sum(rv.Index(i).Interface())
269-
if err != nil {
270-
return nil, err
271-
}
272-
switch elemSum := elemSum.(type) {
273-
case int:
274-
total += elemSum
275-
case float64:
276-
fTotal += elemSum
277-
}
278-
}
279-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
280-
total += int(rv.Int())
281-
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
282-
total += int(rv.Uint())
283-
case reflect.Float32, reflect.Float64:
284-
fTotal += rv.Float()
285-
default:
286-
return nil, fmt.Errorf("invalid argument for sum (type %T)", arg)
287-
}
288-
}
289-
290-
if fTotal != 0.0 {
291-
return fTotal + float64(total), nil
292-
}
293-
return total, nil
294-
}
295-
296257
func minMax(name string, fn func(any, any) bool, args ...any) (any, error) {
297258
var val any
298259
for _, arg := range args {

checker/checker.go

+23
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,29 @@ func (v *checker) BuiltinNode(node *ast.BuiltinNode) (reflect.Type, info) {
668668
}
669669
return v.error(node.Arguments[1], "predicate should has one input and one output param")
670670

671+
case "sum":
672+
collection, _ := v.visit(node.Arguments[0])
673+
if !isArray(collection) && !isAny(collection) {
674+
return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
675+
}
676+
677+
if len(node.Arguments) == 2 {
678+
v.begin(collection)
679+
closure, _ := v.visit(node.Arguments[1])
680+
v.end()
681+
682+
if isFunc(closure) &&
683+
closure.NumOut() == 1 &&
684+
closure.NumIn() == 1 && isAny(closure.In(0)) {
685+
return closure.Out(0), info{}
686+
}
687+
} else {
688+
if isAny(collection) {
689+
return anyType, info{}
690+
}
691+
return collection.Elem(), info{}
692+
}
693+
671694
case "find", "findLast":
672695
collection, _ := v.visit(node.Arguments[0])
673696
if !isArray(collection) && !isAny(collection) {

compiler/compiler.go

+19
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,25 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
809809
c.emit(OpEnd)
810810
return
811811

812+
case "sum":
813+
c.compile(node.Arguments[0])
814+
c.emit(OpBegin)
815+
c.emit(OpInt, 0)
816+
c.emit(OpSetAcc)
817+
c.emitLoop(func() {
818+
if len(node.Arguments) == 2 {
819+
c.compile(node.Arguments[1])
820+
} else {
821+
c.emit(OpPointer)
822+
}
823+
c.emit(OpGetAcc)
824+
c.emit(OpAdd)
825+
c.emit(OpSetAcc)
826+
})
827+
c.emit(OpGetAcc)
828+
c.emit(OpEnd)
829+
return
830+
812831
case "find":
813832
c.compile(node.Arguments[0])
814833
c.emit(OpBegin)

go.mod

+10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ require github.com/stretchr/testify v1.8.4
66

77
require (
88
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/expr-lang/expr/debug v0.0.0-20240303154432-4cdfd385dd04 // indirect
10+
github.com/gdamore/encoding v1.0.0 // indirect
11+
github.com/gdamore/tcell/v2 v2.6.0 // indirect
12+
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
13+
github.com/mattn/go-runewidth v0.0.15 // indirect
914
github.com/pmezard/go-difflib v1.0.0 // indirect
15+
github.com/rivo/tview v0.0.0-20230814110005-ccc2c8119703 // indirect
16+
github.com/rivo/uniseg v0.4.4 // indirect
17+
golang.org/x/sys v0.11.0 // indirect
18+
golang.org/x/term v0.11.0 // indirect
19+
golang.org/x/text v0.12.0 // indirect
1020
gopkg.in/yaml.v3 v3.0.1 // indirect
1121
)

go.sum

+49
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,58 @@
11
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/expr-lang/expr/debug v0.0.0-20240303154432-4cdfd385dd04 h1:Yk8nY2fDkbo5MsfOxwHj/oE70JrY/1ljVCjxSlBhy+s=
4+
github.com/expr-lang/expr/debug v0.0.0-20240303154432-4cdfd385dd04/go.mod h1:UhOtHhf97kA2sqqtmom3F52PEnGgEXrrGNNStcnTPZU=
5+
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
6+
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
7+
github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg=
8+
github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y=
9+
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
10+
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
11+
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
12+
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
13+
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
314
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
415
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
16+
github.com/rivo/tview v0.0.0-20230814110005-ccc2c8119703 h1:ZyM/+FYnpbZsFWuCohniM56kRoHRB4r5EuIzXEYkpxo=
17+
github.com/rivo/tview v0.0.0-20230814110005-ccc2c8119703/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE=
18+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
19+
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
20+
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
21+
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
522
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
623
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
24+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
25+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
26+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
27+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
28+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
29+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
30+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
31+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
32+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
33+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
34+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
35+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
36+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
37+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
38+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
39+
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
40+
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
41+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
42+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
43+
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
44+
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
45+
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
46+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
47+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
48+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
49+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
50+
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
51+
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
52+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
53+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
54+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
55+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
756
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
857
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
958
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

parser/parser.go

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ var predicates = map[string]struct {
3434
"filter": {[]arg{expr, closure}},
3535
"map": {[]arg{expr, closure}},
3636
"count": {[]arg{expr, closure}},
37+
"sum": {[]arg{expr, closure | optional}},
3738
"find": {[]arg{expr, closure}},
3839
"findIndex": {[]arg{expr, closure}},
3940
"findLast": {[]arg{expr, closure}},

test/fuzz/fuzz_corpus.txt

-1
Original file line numberDiff line numberDiff line change
@@ -10455,7 +10455,6 @@ max(f64, i64)
1045510455
max(false ? 1 : 0.5)
1045610456
max(false ? 1 : nil)
1045710457
max(false ? add : ok)
10458-
max(false ? half : list)
1045910458
max(false ? i : nil)
1046010459
max(false ? i32 : score)
1046110460
max(false ? true : 1)

testdata/examples.txt

-14
Original file line numberDiff line numberDiff line change
@@ -7419,12 +7419,6 @@ get(ok ? score : foo, String?.foo())
74197419
get(ok ? score : i64, foo)
74207420
get(reduce(list, array), i32)
74217421
get(sort(array), i32)
7422-
get(sum(array), Qux)
7423-
get(sum(array), String)
7424-
get(sum(array), f32)
7425-
get(sum(array), f64 == list)
7426-
get(sum(array), greet)
7427-
get(sum(array), i)
74287422
get(take(list, i), i64)
74297423
get(true ? "bar" : ok, score(i))
74307424
get(true ? "foo" : half, list)
@@ -7460,7 +7454,6 @@ greet != nil ? list : false
74607454
greet != score
74617455
greet != score != false
74627456
greet != score or ok
7463-
greet != sum(array)
74647457
greet == add
74657458
greet == add ? i : list
74667459
greet == add or ok
@@ -12200,7 +12193,6 @@ last(ok ? ok : 0.5)
1220012193
last(reduce(array, list))
1220112194
last(reduce(list, array))
1220212195
last(sort(array))
12203-
last(sum(array))
1220412196
last(true ? "bar" : half)
1220512197
last(true ? add : list)
1220612198
last(true ? foo : 1)
@@ -14818,7 +14810,6 @@ ok != nil ? nil : array
1481814810
ok != not ok
1481914811
ok != ok
1482014812
ok != ok ? false : "bar"
14821-
ok != sum(array)
1482214813
ok && !false
1482314814
ok && !ok
1482414815
ok && "foo" matches "bar"
@@ -16970,7 +16961,6 @@ string(groupBy(list, i))
1697016961
string(half != nil)
1697116962
string(half != score)
1697216963
string(half == nil)
16973-
string(half == sum(array))
1697416964
string(half(0.5))
1697516965
string(half(1))
1697616966
string(half(f64))
@@ -17297,18 +17287,14 @@ sum([0.5])
1729717287
sum([f32])
1729817288
sum(array)
1729917289
sum(array) != f32
17300-
sum(array) != half
17301-
sum(array) != ok
1730217290
sum(array) % i
1730317291
sum(array) % i64
1730417292
sum(array) - f32
1730517293
sum(array) / -f64
1730617294
sum(array) < i
17307-
sum(array) == div
1730817295
sum(array) == i64 - i
1730917296
sum(array) ^ f64
1731017297
sum(array) not in array
17311-
sum(array) not in list
1731217298
sum(filter(array, ok))
1731317299
sum(groupBy(array, i32).String)
1731417300
sum(groupBy(list, #)?.greet)

0 commit comments

Comments
 (0)