diff --git a/src/apfl.h b/src/apfl.h index 47593e1..71ed0cb 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -655,6 +655,8 @@ enum apfl_value_type apfl_get_type(apfl_ctx, apfl_stackidx); // Drop a value from the stack void apfl_drop(apfl_ctx, apfl_stackidx); +// Move a value to the top of the stack +void apfl_move_to_top_of_stack(apfl_ctx, apfl_stackidx); // Push a nil onto the stack void apfl_push_nil(apfl_ctx); // Push a boolean onto the stack @@ -684,6 +686,8 @@ size_t apfl_len(apfl_ctx, apfl_stackidx); // Get a string view into a APFL_VALUE_STRING value. The value stays on the stack. // The returned string view is only guaranteed to be valid, as long as the value is on the stack. struct apfl_string_view apfl_get_string(apfl_ctx, apfl_stackidx); +// Pops a value off the stack and turns it into a string. +void apfl_tostring(apfl_ctx, apfl_stackidx); // Pops a value from the stack and returns whether is was truthy. All values except false and nil are truthy. bool apfl_is_truthy(apfl_ctx, apfl_stackidx); // Pops a number from the stack and returns it. diff --git a/src/context.c b/src/context.c index 70a2592..3bdbb04 100644 --- a/src/context.c +++ b/src/context.c @@ -419,6 +419,14 @@ current_stack_move_to_top(apfl_ctx ctx, apfl_stackidx index) return true; } +void +apfl_move_to_top_of_stack(apfl_ctx ctx, apfl_stackidx index) +{ + if (!current_stack_move_to_top(ctx, index)) { + apfl_raise_invalid_stackidx(ctx); + } +} + void apfl_stack_clear(apfl_ctx ctx) { @@ -1102,6 +1110,33 @@ apfl_get_type(apfl_ctx ctx, apfl_stackidx index) return apfl_value_type_to_abstract_type(value.type); } +void +apfl_tostring(apfl_ctx ctx, apfl_stackidx index) +{ + apfl_move_to_top_of_stack(ctx, index); + struct apfl_value value = apfl_stack_must_get(ctx, -1); + if (apfl_value_type_to_abstract_type(value.type) == APFL_VALUE_STRING) { + return; + } + + struct apfl_string_builder sb = apfl_string_builder_init(ctx->gc.allocator); + if (!apfl_value_format(value, apfl_format_string_writer(&sb))) { + apfl_string_builder_deinit(&sb); + apfl_stack_drop(ctx, -1); + apfl_raise_alloc_error(ctx); + } + + 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); + apfl_stack_drop(ctx, -1); + apfl_raise_alloc_error(ctx); + } + + apfl_stack_drop(ctx, -2); // Drop original value +} + bool apfl_is_truthy(apfl_ctx ctx, apfl_stackidx index) { diff --git a/src/globals.c b/src/globals.c index 99480cc..c510808 100644 --- a/src/globals.c +++ b/src/globals.c @@ -155,6 +155,7 @@ print(apfl_ctx ctx) } apfl_get_list_member_by_index(ctx, 0, i); + apfl_tostring(ctx, -1); struct apfl_string_view sv = apfl_get_string(ctx, -1); TRY_FORMAT(ctx, apfl_format_put_string(w, sv)); apfl_drop(ctx, -1); @@ -204,6 +205,19 @@ disasm(apfl_ctx ctx) } } +static void +tostring(apfl_ctx ctx) +{ + size_t len = apfl_len(ctx, 0); + if (len != 1) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, "tostring needs exactly 1 argument"); + } + + apfl_get_list_member_by_index(ctx, 0, 0); + apfl_drop(ctx, 0); + apfl_tostring(ctx, -1); +} + static const struct global_def globals[] = { {"if", impl_if}, {"==", impl_eq}, @@ -214,6 +228,7 @@ static const struct global_def globals[] = { {"print", print}, {"dump", dump}, {"disasm", disasm}, + {"tostring", tostring}, {NULL, NULL}, }; diff --git a/src/value.c b/src/value.c index 9db9e75..19c8460 100644 --- a/src/value.c +++ b/src/value.c @@ -274,10 +274,16 @@ apfl_value_move(struct apfl_value *src) return out; } +bool +apfl_value_format(struct apfl_value value, struct apfl_format_writer w) +{ + return format(0, w, value, false); +} + bool apfl_value_print(struct apfl_value value, struct apfl_format_writer w) { - TRY(format(0, w, value, false)); + TRY(apfl_value_format(value, w)); TRY(apfl_format_put_string(w, "\n")); return true; } diff --git a/src/value.h b/src/value.h index 98a2d89..0dbe8be 100644 --- a/src/value.h +++ b/src/value.h @@ -82,6 +82,7 @@ enum apfl_value_type apfl_value_type_to_abstract_type(enum value_type); bool apfl_value_eq(const struct apfl_value, const struct apfl_value); struct apfl_value apfl_value_move(struct apfl_value *src); +bool apfl_value_format(struct apfl_value, struct apfl_format_writer); bool apfl_value_print(struct apfl_value, struct apfl_format_writer); apfl_hash apfl_value_hash(const struct apfl_value);