diff --git a/meson.build b/meson.build index 330d7899f..68c9d9a4c 100644 --- a/meson.build +++ b/meson.build @@ -271,6 +271,7 @@ test_files = [ 'test/suite-struct.janet', 'test/suite-symcache.janet', 'test/suite-table.janet', + 'test/suite-tuple.janet', 'test/suite-unknown.janet', 'test/suite-value.janet', 'test/suite-vm.janet' diff --git a/src/core/array.c b/src/core/array.c index 32ee5dfa3..2eb52b6e7 100644 --- a/src/core/array.c +++ b/src/core/array.c @@ -275,6 +275,31 @@ JANET_CORE_FN(cfun_array_concat, return janet_wrap_array(array); } +JANET_CORE_FN(cfun_array_join, + "(array/join arr & parts)", + "Join a variable number of arrays and tuples into the first argument, " + "which must be an array. " + "Return the modified array `arr`.") { + int32_t i; + janet_arity(argc, 1, -1); + JanetArray *array = janet_getarray(argv, 0); + for (i = 1; i < argc; i++) { + int32_t j, len = 0; + const Janet *vals = NULL; + if (!janet_indexed_view(argv[i], &vals, &len)) { + janet_panicf("expected indexed type for argument %d, got %v", i, argv[i]); + } + if (array->data == vals) { + int32_t newcount = array->count + len; + janet_array_ensure(array, newcount, 2); + janet_indexed_view(argv[i], &vals, &len); + } + for (j = 0; j < len; j++) + janet_array_push(array, vals[j]); + } + return janet_wrap_array(array); +} + JANET_CORE_FN(cfun_array_insert, "(array/insert arr at & xs)", "Insert all `xs` into array `arr` at index `at`. `at` should be an integer between " @@ -385,6 +410,7 @@ void janet_lib_array(JanetTable *env) { JANET_CORE_REG("array/remove", cfun_array_remove), JANET_CORE_REG("array/trim", cfun_array_trim), JANET_CORE_REG("array/clear", cfun_array_clear), + JANET_CORE_REG("array/join", cfun_array_join), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, array_cfuns); diff --git a/src/core/tuple.c b/src/core/tuple.c index bf6d09e6e..c67c94a0d 100644 --- a/src/core/tuple.c +++ b/src/core/tuple.c @@ -116,6 +116,34 @@ JANET_CORE_FN(cfun_tuple_setmap, return argv[0]; } +JANET_CORE_FN(cfun_tuple_join, + "(tuple/join & parts)", + "Create a tuple by joining together other tuples and arrays.") { + janet_arity(argc, 0, -1); + int32_t total_len = 0; + for (int32_t i = 0; i < argc; i++) { + int32_t len = 0; + const Janet *vals = NULL; + if (!janet_indexed_view(argv[i], &vals, &len)) { + janet_panicf("expected indexed type for argument %d, got %v", i, argv[i]); + } + if (INT32_MAX - total_len < len) { + janet_panic("tuple too large"); + } + total_len += len; + } + Janet *tup = janet_tuple_begin(total_len); + Janet *tup_cursor = tup; + for (int32_t i = 0; i < argc; i++) { + int32_t len = 0; + const Janet *vals = NULL; + janet_indexed_view(argv[i], &vals, &len); + memcpy(tup_cursor, vals, len * sizeof(Janet)); + tup_cursor += len; + } + return janet_wrap_tuple(janet_tuple_end(tup)); +} + /* Load the tuple module */ void janet_lib_tuple(JanetTable *env) { JanetRegExt tuple_cfuns[] = { @@ -124,6 +152,7 @@ void janet_lib_tuple(JanetTable *env) { JANET_CORE_REG("tuple/type", cfun_tuple_type), JANET_CORE_REG("tuple/sourcemap", cfun_tuple_sourcemap), JANET_CORE_REG("tuple/setmap", cfun_tuple_setmap), + JANET_CORE_REG("tuple/join", cfun_tuple_join), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, tuple_cfuns); diff --git a/test/suite-array.janet b/test/suite-array.janet index 0b02ab1ec..0cafd20b7 100644 --- a/test/suite-array.janet +++ b/test/suite-array.janet @@ -76,6 +76,16 @@ (array/trim a) (array/ensure @[1 1] 6 2) +# array/join +(assert (deep= @[1 2 3] (array/join @[] [1] [2] [3])) "array/join 1") +(assert (deep= @[] (array/join @[])) "array/join 2") +(assert (deep= @[1 :a :b :c] (array/join @[1] @[:a :b] [] [:c])) "array/join 3") +(assert (deep= @[:x :y :z "abc123" "def456"] (array/join @[:x :y :z] ["abc123" "def456"])) "array/join 4") +(assert-error "array/join error 1" (array/join)) +(assert-error "array/join error 2" (array/join [])) +(assert-error "array/join error 3" (array/join [] "abc123")) +(assert-error "array/join error 4" (array/join @[] "abc123")) +(assert-error "array/join error 5" (array/join @[] "abc123")) (end-suite) diff --git a/test/suite-tuple.janet b/test/suite-tuple.janet new file mode 100644 index 000000000..6a74e9ece --- /dev/null +++ b/test/suite-tuple.janet @@ -0,0 +1,30 @@ +# Copyright (c) 2023 Calvin Rose +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +(import ./helper :prefix "" :exit true) +(start-suite) + +(assert (= [1 2 3] (tuple/join [1] [2] [3])) "tuple/join 1") +(assert (= [] (tuple/join)) "tuple/join 2") +(assert (= [:a :b :c] (tuple/join @[:a :b] [] [:c])) "tuple/join 3") +(assert (= ["abc123" "def456"] (tuple/join ["abc123" "def456"])) "tuple/join 4") + +(end-suite) +