Implement global slice and splice functions
This also adds the apfl_list_splice() function for manipulating lists.
This commit is contained in:
parent
85d2c67404
commit
ddd39f19ef
10 changed files with 227 additions and 5 deletions
|
|
@ -136,6 +136,8 @@ functionaltest("get-optional")
|
|||
functionaltest("has-key")
|
||||
functionaltest("tonumber")
|
||||
functionaltest("re")
|
||||
functionaltest("slice")
|
||||
functionaltest("splice")
|
||||
|
||||
install(TARGETS apfl DESTINATION lib)
|
||||
install(TARGETS apfl-bin DESTINATION bin)
|
||||
|
|
|
|||
14
src/apfl.h
14
src/apfl.h
|
|
@ -770,6 +770,19 @@ void apfl_list_create(apfl_ctx, size_t initial_capacity);
|
|||
void apfl_list_append(apfl_ctx, apfl_stackidx list, apfl_stackidx value);
|
||||
// Append a list to another list. The second list will be dropped.
|
||||
void apfl_list_append_list(apfl_ctx, apfl_stackidx a, apfl_stackidx b);
|
||||
// Splice a list into another list. Removes splice_len elements from list,
|
||||
// starting at splice_off and inserts b_len elements from other (starting at
|
||||
// b_off) at that place. list and other will be popped, a new list will be
|
||||
// pushed.
|
||||
void apfl_list_splice(
|
||||
apfl_ctx,
|
||||
apfl_stackidx list,
|
||||
size_t splice_off,
|
||||
size_t splice_len,
|
||||
apfl_stackidx other,
|
||||
size_t b_off,
|
||||
size_t b_len
|
||||
);
|
||||
// Create a new empty dict on the stack
|
||||
void apfl_dict_create(apfl_ctx);
|
||||
// Set a value in a dictionary. k and v will be dropped.
|
||||
|
|
@ -913,6 +926,7 @@ struct apfl_messages {
|
|||
const char *invalid_matcher_state;
|
||||
const char *no_matching_subfunction;
|
||||
const char *invalid_call_stack_index;
|
||||
const char *splice_arguments_out_of_range;
|
||||
};
|
||||
extern const struct apfl_messages apfl_messages;
|
||||
|
||||
|
|
|
|||
|
|
@ -763,6 +763,93 @@ symbol(apfl_ctx ctx)
|
|||
}
|
||||
}
|
||||
|
||||
struct splice_info {
|
||||
size_t off;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static apfl_number
|
||||
getnumarg(apfl_ctx ctx, size_t arg)
|
||||
{
|
||||
apfl_get_list_member_by_index(ctx, 0, arg);
|
||||
return apfl_get_number(ctx, -1);
|
||||
}
|
||||
|
||||
static size_t
|
||||
splice_off(apfl_ctx ctx, size_t list_len, size_t arg)
|
||||
{
|
||||
size_t off;
|
||||
apfl_number off_num = getnumarg(ctx, arg);
|
||||
if (off_num < 0) {
|
||||
off = (size_t)(-off_num);
|
||||
off = off > list_len ? 0 : list_len - off;
|
||||
} else {
|
||||
off = (size_t)off_num;
|
||||
if (off >= list_len) {
|
||||
off = list_len;
|
||||
}
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
static size_t
|
||||
splice_len(apfl_ctx ctx, size_t list_len, size_t off, size_t arg)
|
||||
{
|
||||
assert(off <= list_len);
|
||||
list_len -= off;
|
||||
|
||||
apfl_get_list_member_by_index(ctx, 0, arg);
|
||||
if (apfl_get_type(ctx, -1) == APFL_VALUE_NIL) {
|
||||
apfl_drop(ctx, -1);
|
||||
return list_len;
|
||||
}
|
||||
|
||||
apfl_number num = apfl_get_number(ctx, -1);
|
||||
if (num < 0) {
|
||||
size_t len = (size_t)(-num);
|
||||
return len > list_len ? 0 : list_len - len;
|
||||
}
|
||||
|
||||
size_t len = (size_t)num;
|
||||
return len > list_len ? list_len : len;
|
||||
}
|
||||
|
||||
static struct splice_info
|
||||
splice_get_list(apfl_ctx ctx, size_t args_base)
|
||||
{
|
||||
apfl_get_list_member_by_index(ctx, 0, args_base);
|
||||
if (apfl_get_type(ctx, -1) != APFL_VALUE_LIST) {
|
||||
apfl_raise_errorfmt(
|
||||
ctx,
|
||||
"Expected a list argument to splice, got {stack:type} instead",
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
size_t list_len = apfl_len(ctx, -1);
|
||||
|
||||
size_t off = splice_off(ctx, list_len, args_base + 1);
|
||||
size_t len = splice_len(ctx, list_len, off, args_base + 2);
|
||||
|
||||
return (struct splice_info) {
|
||||
.off = off,
|
||||
.len = len,
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
splice(apfl_ctx ctx)
|
||||
{
|
||||
if (apfl_len(ctx, 0) != 6) {
|
||||
apfl_raise_const_error(ctx, "splice needs exactly 6 arguments");
|
||||
}
|
||||
|
||||
struct splice_info a = splice_get_list(ctx, 0);
|
||||
struct splice_info b = splice_get_list(ctx, 3);
|
||||
|
||||
apfl_list_splice(ctx, -2, a.off, a.len, -1, b.off, b.len);
|
||||
}
|
||||
|
||||
static void
|
||||
add_builtin(apfl_ctx ctx, const char *name, apfl_cfunc func)
|
||||
{
|
||||
|
|
@ -854,4 +941,5 @@ apfl_builtins(apfl_ctx ctx)
|
|||
add_builtin(ctx, "get-argv", get_argv);
|
||||
add_builtin(ctx, "cmod-searcher", cmod_searcher);
|
||||
add_builtin(ctx, "tonumber", tonumber);
|
||||
add_builtin(ctx, "splice", splice);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1205,7 +1205,7 @@ apfl_list_append(apfl_ctx ctx, apfl_stackidx list_index, apfl_stackidx value_ind
|
|||
|
||||
bool ok;
|
||||
|
||||
if (!apfl_list_splice(
|
||||
if (!apfl_list_splice_raw(
|
||||
&ctx->gc,
|
||||
&list_val->list,
|
||||
list_val->list->len,
|
||||
|
|
@ -1243,7 +1243,7 @@ apfl_list_append_list(apfl_ctx ctx, apfl_stackidx dst_index, apfl_stackidx src_i
|
|||
apfl_raise_const_error(ctx, apfl_messages.not_a_list);
|
||||
}
|
||||
|
||||
if (!apfl_list_splice(
|
||||
if (!apfl_list_splice_raw(
|
||||
&ctx->gc,
|
||||
&dst_val->list,
|
||||
dst_val->list->len,
|
||||
|
|
@ -1520,7 +1520,7 @@ apfl_set_list_member_by_index(
|
|||
|
||||
struct apfl_value value = apfl_stack_must_get(ctx, value_index);
|
||||
|
||||
if (!apfl_list_splice(
|
||||
if (!apfl_list_splice_raw(
|
||||
&ctx->gc,
|
||||
&list->list,
|
||||
index_in_list,
|
||||
|
|
@ -1534,6 +1534,60 @@ apfl_set_list_member_by_index(
|
|||
apfl_drop(ctx, value_index);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_list_splice(
|
||||
apfl_ctx ctx,
|
||||
apfl_stackidx list,
|
||||
size_t cut_off,
|
||||
size_t cut_count,
|
||||
apfl_stackidx other,
|
||||
size_t other_off,
|
||||
size_t other_count
|
||||
) {
|
||||
apfl_multi_move_to_top_of_stack(ctx, 2, (apfl_stackidx[]) {other, list});
|
||||
|
||||
struct apfl_value *list_val = stack_get_pointer(ctx, -1);
|
||||
struct apfl_value *other_val = stack_get_pointer(ctx, -2);
|
||||
|
||||
if (list_val->type != VALUE_LIST) {
|
||||
apfl_raise_errorfmt(ctx, "Expected list, got {value:type}", *list_val);
|
||||
}
|
||||
struct list_header** list_listp = &list_val->list;
|
||||
if (other_val->type != VALUE_LIST) {
|
||||
apfl_raise_errorfmt(ctx, "Expected list, got {value:type}", *other_val);
|
||||
}
|
||||
struct list_header* other_list = other_val->list;
|
||||
|
||||
if (!apfl_resizable_check_cut_args((*list_listp)->len, cut_off, cut_count)) {
|
||||
apfl_raise_const_error(ctx, apfl_messages.splice_arguments_out_of_range);
|
||||
}
|
||||
|
||||
struct apfl_value *other_values = NULL;
|
||||
if (other_count > 0) {
|
||||
if (
|
||||
other_off >= other_list->len
|
||||
|| other_count > (other_list->len - other_off)
|
||||
) {
|
||||
apfl_raise_const_error(ctx, apfl_messages.splice_arguments_out_of_range);
|
||||
}
|
||||
|
||||
other_values = &(other_list->items[other_off]);
|
||||
}
|
||||
|
||||
if (!apfl_list_splice_raw(
|
||||
&ctx->gc,
|
||||
list_listp,
|
||||
cut_off,
|
||||
cut_count,
|
||||
other_values,
|
||||
other_count
|
||||
)) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
apfl_drop(ctx, -2);
|
||||
}
|
||||
|
||||
size_t
|
||||
apfl_len(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
|
|
|
|||
27
src/functional-tests/slice.at
Normal file
27
src/functional-tests/slice.at
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
===== script =====
|
||||
test := { ~args ->
|
||||
print (& "[" (join " " (slice ~args)) "]")
|
||||
}
|
||||
|
||||
test 0 [1 2 3 4]
|
||||
test 1 [1 2 3 4]
|
||||
test -1 [1 2 3 4]
|
||||
test 1 2 [1 2 3 4]
|
||||
test 1 3 [1 2 3 4]
|
||||
test 1 10000 [1 2 3 4]
|
||||
test 1 -1 [1 2 3 4]
|
||||
test 0 0 [1 2 3 4]
|
||||
test 4 1 [1 2 3 4]
|
||||
test -4 1 [1 2 3 4]
|
||||
|
||||
===== output =====
|
||||
[1 2 3 4]
|
||||
[2 3 4]
|
||||
[4]
|
||||
[2 3]
|
||||
[2 3 4]
|
||||
[2 3 4]
|
||||
[2 3]
|
||||
[]
|
||||
[]
|
||||
[1]
|
||||
19
src/functional-tests/splice.at
Normal file
19
src/functional-tests/splice.at
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
===== script =====
|
||||
test := { ~args ->
|
||||
print (& "[" (join " " (splice ~args)) "]")
|
||||
}
|
||||
|
||||
test [1 2 3 4 5] 0 nil
|
||||
test [1 2 3 4 5] 100
|
||||
test [1 2 3 4 5] 1 -2
|
||||
test [1 2 3 4 5] 1 -2 [100 200 300]
|
||||
test [1 2 3 4 5] 1 -2 [100 200 300] 1
|
||||
test [1 2 3 4 5] 1 -2 [100 200 300] 1 -1
|
||||
|
||||
===== output =====
|
||||
[]
|
||||
[1 2 3 4 5]
|
||||
[1 4 5]
|
||||
[1 100 200 300 4 5]
|
||||
[1 200 300 4 5]
|
||||
[1 200 4 5]
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
get-optional := C.get-optional
|
||||
raise := C.raise
|
||||
symbol := C.symbol
|
||||
splice := C.splice
|
||||
-serialize-bytecode := C.-serialize-bytecode
|
||||
-unserialize-bytecode := C.-unserialize-bytecode
|
||||
|
||||
|
|
@ -309,6 +310,20 @@
|
|||
[(f x) ~(map f xs)]
|
||||
}
|
||||
|
||||
splice := {
|
||||
a off -> splice a off nil
|
||||
a off len -> splice a off len []
|
||||
a a-off a-len b -> splice a a-off a-len b 0 nil
|
||||
a a-off a-len b b-off -> splice a a-off a-len b b-off nil
|
||||
a a-off a-len b b-off b-len ->
|
||||
C.splice a a-off a-len b b-off b-len
|
||||
}
|
||||
|
||||
slice := {
|
||||
off l -> slice off nil l
|
||||
off len l -> splice [] 0 nil l off len
|
||||
}
|
||||
|
||||
# Dictionary of exported functions
|
||||
[
|
||||
'if -> if
|
||||
|
|
@ -374,5 +389,7 @@
|
|||
'unwrap-some -> unwrap-some
|
||||
'import -> modules.import
|
||||
'map -> map
|
||||
'splice -> splice
|
||||
'slice -> slice
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,4 +19,5 @@ const struct apfl_messages apfl_messages = {
|
|||
.invalid_matcher_state = "Matcher is in invalid state",
|
||||
.no_matching_subfunction = "No matching subfunction",
|
||||
.invalid_call_stack_index = "Invalid call stack index",
|
||||
.splice_arguments_out_of_range = "Splice arguments out of range",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -613,7 +613,7 @@ apfl_list_deinit(struct apfl_allocator allocator, struct list_header *list)
|
|||
}
|
||||
|
||||
bool
|
||||
apfl_list_splice(
|
||||
apfl_list_splice_raw(
|
||||
struct gc *gc,
|
||||
struct list_header **dst_ptr,
|
||||
size_t cut_start,
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ size_t apfl_list_len(struct list_header *);
|
|||
void apfl_list_deinit(struct apfl_allocator, struct list_header *);
|
||||
|
||||
bool
|
||||
apfl_list_splice(
|
||||
apfl_list_splice_raw(
|
||||
struct gc *gc,
|
||||
struct list_header **dst_ptr,
|
||||
size_t cut_start,
|
||||
|
|
|
|||
Loading…
Reference in a new issue