Implement functions for concatenating strings

This commit is contained in:
Laria 2022-11-20 16:26:38 +01:00
parent f9878b43d8
commit e8a92a18b4
10 changed files with 206 additions and 14 deletions

View file

@ -70,6 +70,8 @@ functionaltest("dictionary-assignments")
functionaltest("variadic-functions")
functionaltest("predicate")
functionaltest("compare")
functionaltest("concat")
functionaltest("join")
install(TARGETS apfl DESTINATION lib)
install(TARGETS apfl-bin DESTINATION bin)

View file

@ -108,6 +108,7 @@ struct apfl_string_builder {
struct apfl_string_builder apfl_string_builder_init(struct apfl_allocator allocator);
void apfl_string_builder_deinit(struct apfl_string_builder *);
bool apfl_string_builder_prealloc(struct apfl_string_builder *, size_t newcap);
bool apfl_string_builder_append(struct apfl_string_builder *, struct apfl_string_view);
bool apfl_string_builder_append_byte(struct apfl_string_builder *, char byte);
bool apfl_string_builder_append_bytes(struct apfl_string_builder *, const char *bytes, size_t len);
@ -669,6 +670,10 @@ void apfl_push_number(apfl_ctx, apfl_number);
void apfl_push_string_view_copy(apfl_ctx, struct apfl_string_view);
// Push a constant string.
void apfl_push_const_string(apfl_ctx, const char *);
// Joins all elements of the list parts together with glue into a string that is pushed onto the stack.
// glue and parts are popped off the stack. glue and the elements of parts are converted into a string if they are not
// already one.
void apfl_join_strings(apfl_ctx, apfl_stackidx glue, apfl_stackidx parts);
// Create a new empty list on the stack
void apfl_list_create(apfl_ctx, size_t initial_capacity);
// Append a value to a list. The value will be dropped.

View file

@ -1156,10 +1156,9 @@ apfl_len(apfl_ctx ctx, apfl_stackidx index)
return 0;
}
struct apfl_string_view
apfl_get_string(apfl_ctx ctx, apfl_stackidx index)
static bool
get_string_view_of_value(struct apfl_string_view *sv, struct apfl_value value)
{
struct apfl_value value = apfl_stack_must_get(ctx, index);
switch (value.type) {
case VALUE_NIL:
case VALUE_BOOLEAN:
@ -1169,17 +1168,27 @@ apfl_get_string(apfl_ctx ctx, apfl_stackidx index)
case VALUE_USERDATA:
case VALUE_LIST:
case VALUE_DICT:
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type);
goto error;
return false;
case VALUE_STRING:
return apfl_string_view_from(*value.string);
*sv = apfl_string_view_from(*value.string);
return true;
case VALUE_CONST_STRING:
return value.const_string;
*sv = value.const_string;
return true;
}
error:
assert(false);
return (struct apfl_string_view) {.bytes = NULL, .len = 0};
return false;
}
struct apfl_string_view
apfl_get_string(apfl_ctx ctx, apfl_stackidx index)
{
struct apfl_string_view sv;
if (!get_string_view_of_value(&sv, apfl_stack_must_get(ctx, index))) {
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type);
}
return sv;
}
enum apfl_value_type
@ -1216,6 +1225,94 @@ apfl_tostring(apfl_ctx ctx, apfl_stackidx index)
apfl_stack_drop(ctx, -2); // Drop original value
}
static void
join_strings_inner(apfl_ctx ctx, apfl_stackidx _glue, apfl_stackidx _parts)
{
struct apfl_value glue_val = apfl_stack_must_get(ctx, _glue);
struct apfl_value parts_val = apfl_stack_must_get(ctx, _parts);
if (!apfl_value_add_as_tmproot(&ctx->gc, glue_val)) {
apfl_raise_alloc_error(ctx);
}
if (!apfl_value_add_as_tmproot(&ctx->gc, parts_val)) {
apfl_raise_alloc_error(ctx);
}
assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]) { _glue, _parts }));
if (parts_val.type != VALUE_LIST) {
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.not_a_list);
}
struct list_header *parts = parts_val.list;
apfl_stack_must_push(ctx, glue_val);
apfl_tostring(ctx, -1);
struct apfl_string_view glue_sv = apfl_get_string(ctx, -1);
size_t total_length = 0;
for (size_t i = 0; i < parts->len; i++) {
apfl_stack_must_push(ctx, parts->items[i]);
apfl_tostring(ctx, -1);
parts->items[i] = apfl_stack_must_pop(ctx, -1);
struct apfl_string_view part_sv;
assert(get_string_view_of_value(&part_sv, parts->items[i]) /* Value should be a string at this point */);
total_length += part_sv.len;
if (i > 0) {
total_length += glue_sv.len;
}
}
struct apfl_string_builder sb = apfl_string_builder_init(ctx->gc.allocator);
if (!apfl_string_builder_prealloc(&sb, total_length)) {
goto error;
}
for (size_t i = 0; i < parts->len; i++) {
if (i > 0) {
if (!apfl_string_builder_append(&sb, glue_sv)) {
goto error;
}
}
struct apfl_string_view part_sv;
assert(get_string_view_of_value(&part_sv, parts->items[i]) /* Value should be a string at this point */);
if (!apfl_string_builder_append(&sb, part_sv)) {
goto error;
}
}
struct apfl_string str = apfl_string_builder_move_string(&sb);
apfl_string_builder_deinit(&sb);
if (!apfl_move_string_onto_stack(ctx, str)) {
apfl_string_deinit(ctx->gc.allocator, &str);
goto error;
}
apfl_drop(ctx, -2); // Drop the glue from the stack
return;
error:
apfl_drop(ctx, -1); // Drop the glue from the stack
apfl_raise_alloc_error(ctx);
}
void
apfl_join_strings(apfl_ctx ctx, apfl_stackidx glue, apfl_stackidx parts)
{
size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc);
join_strings_inner(ctx, glue, parts);
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
}
bool
apfl_is_truthy(apfl_ctx ctx, apfl_stackidx index)
{

View file

@ -419,11 +419,8 @@ evaluate_until_call_stack_return(apfl_ctx ctx)
static void
must_tmproot_add_value(apfl_ctx ctx, struct apfl_value value)
{
struct gc_object *obj = apfl_value_get_gc_object(value);
if (obj != NULL) {
if (!apfl_gc_tmproot_add(&ctx->gc, obj)) {
apfl_raise_alloc_error(ctx);
}
if (!apfl_value_add_as_tmproot(&ctx->gc, value)) {
apfl_raise_alloc_error(ctx);
}
}

View file

@ -0,0 +1,13 @@
===== script =====
dump (&)
dump (& 'foo)
dump (& 'foo 'bar)
dump (& 'foo 'bar 'baz)
dump (& 'foo 1 true [] [->])
===== output =====
""
"foo"
"foobar"
"foobarbaz"
"foo1true[][->]"

View file

@ -0,0 +1,35 @@
===== script =====
print (join "," [])
print (join "," ['foo])
print (join "," ['foo 'bar])
print (join "," ['foo 'bar 'baz])
print (join "," ['foo 1 true [] [->]])
print (join ":::" [])
print (join ":::" ['foo])
print (join ":::" ['foo 'bar])
print (join ":::" ['foo 'bar 'baz])
print (join ":::" ['foo 1 true [] [->]])
print (join 1 [])
print (join 1 ['foo])
print (join 1 ['foo 'bar])
print (join 1 ['foo 'bar 'baz])
print (join 1 ['foo 1 true [] [->]])
===== output =====
foo
foo,bar
foo,bar,baz
foo,1,true,[],[->]
foo
foo:::bar
foo:::bar:::baz
foo:::1:::true:::[]:::[->]
foo
foo1bar
foo1bar1baz
foo111true1[]1[->]

View file

@ -169,6 +169,27 @@ op_div(apfl_ctx ctx, apfl_number a, apfl_number b)
IMPL_MATH_OP_FUNC(impl_div, "/", single_identity, op_div)
static void
impl_concat(apfl_ctx ctx)
{
apfl_push_const_string(ctx, "");
apfl_join_strings(ctx, -1, -2);
}
static void
impl_join(apfl_ctx ctx)
{
size_t len = apfl_len(ctx, 0);
if (len != 2) {
apfl_raise_const_error(ctx, APFL_RESULT_ERR, "join expects exactly 2 arguments");
}
apfl_get_list_member_by_index(ctx, 0, 0);
apfl_get_list_member_by_index(ctx, 0, 1);
apfl_drop(ctx, 0);
apfl_join_strings(ctx, -2, -1);
}
static void
print(apfl_ctx ctx)
{
@ -366,6 +387,8 @@ static const struct global_def globals[] = {
{"-", impl_minus},
{"*", impl_mult},
{"/", impl_div},
{"&", impl_concat},
{"join", impl_join},
{"print", print},
{"dump", dump},
{"disasm", disasm},

View file

@ -133,6 +133,18 @@ apfl_string_builder_append_bytes(struct apfl_string_builder *builder, const char
);
}
bool
apfl_string_builder_prealloc(struct apfl_string_builder *builder, size_t newcap)
{
return apfl_resizable_ensure_cap(
builder->allocator,
sizeof(char),
(void **)&builder->bytes,
&builder->cap,
newcap
);
}
bool
apfl_string_builder_append(struct apfl_string_builder *builder, struct apfl_string_view view)
{

View file

@ -724,6 +724,13 @@ apfl_value_visit_gc_object(struct apfl_value value, gc_visitor cb, void *opaque)
}
}
bool
apfl_value_add_as_tmproot(struct gc *gc, struct apfl_value value)
{
struct gc_object *obj = apfl_value_get_gc_object(value);
return obj == NULL ? true : apfl_gc_tmproot_add(gc, obj);
}
void
apfl_gc_list_traverse(struct list_header *list, gc_visitor cb, void *opaque)
{

View file

@ -144,6 +144,7 @@ void apfl_cfunction_deinit(struct apfl_allocator, struct cfunction *);
// Functions for garbage collection
struct gc_object *apfl_value_get_gc_object(struct apfl_value);
void apfl_value_visit_gc_object(struct apfl_value value, gc_visitor cb, void *opaque);
bool apfl_value_add_as_tmproot(struct gc *, struct apfl_value);
void apfl_gc_list_traverse(struct list_header *, gc_visitor, void *);
void apfl_gc_dict_traverse(struct dict_header *, gc_visitor, void *);
void apfl_gc_func_traverse(struct function*, gc_visitor, void *);