From 842d712a3181ddb0e35f5395ede7bb9b9e5385a2 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Tue, 10 Jan 2023 18:47:03 -0800 Subject: [PATCH] Fix bug in S_MOD The 3rd argument to the instruction indicates the type of the second argument. If there are multiple specifiers in the format string, the first specifier matching the given type is substituted (NOT the first specifier encountered when processing the format string). --- include/vm.h | 2 ++ src/format.c | 33 +++++++++++++++++++++++---------- src/parts/parts.c | 2 +- src/vm.c | 4 ++-- subprojects/libsys4 | 2 +- test/Source/strings.jaf | 1 + 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/vm.h b/include/vm.h index 71abbe6..b1bca0c 100644 --- a/include/vm.h +++ b/include/vm.h @@ -113,6 +113,8 @@ bool library_function_exists(int libno, int fno); void init_libraries(void); void exit_libraries(void); +struct string *string_format(struct string *fmt, union vm_value arg, enum ain_data_type type); + void vm_stack_trace(void); _Noreturn void _vm_error(const char *fmt, ...); _Noreturn void vm_exit(int code); diff --git a/src/format.c b/src/format.c index 686a8c0..052dc6c 100644 --- a/src/format.c +++ b/src/format.c @@ -17,6 +17,7 @@ #include #include #include "system4.h" +#include "system4/ain.h" #include "system4/string.h" #include "vm.h" #include "vm/heap.h" @@ -53,35 +54,43 @@ static int read_number(const char **_fmt) return n; } -static void parse_fmt_spec(const char **_fmt, struct fmt_spec *spec) +static bool parse_fmt_spec(const char **_fmt, struct fmt_spec *spec, enum ain_data_type target) { - const char *fmt = *_fmt; + bool r = false; + const char *fmt = (*_fmt) + 1; memset(spec, 0, sizeof(struct fmt_spec)); spec->precision = 6; while (*fmt) { switch (*fmt) { case 'd': spec->type = FMT_INT; + r = target == AIN_INT; goto end; case 'D': spec->type = FMT_INT; spec->zenkaku = true; + r = target == AIN_INT; goto end; case 'f': spec->type = FMT_FLOAT; + r = target == AIN_FLOAT; goto end; case 'F': spec->type = FMT_FLOAT; spec->zenkaku = true; + r = target == AIN_FLOAT; goto end; case 's': spec->type = FMT_STRING; + r = target == AIN_STRING; goto end; case 'c': spec->type = FMT_CHAR; + r = target == AIN_INT; goto end; case 'b': spec->type = FMT_BOOL; + r = target == AIN_INT; goto end; case '0': spec->zero_pad = true; @@ -109,7 +118,9 @@ static void parse_fmt_spec(const char **_fmt, struct fmt_spec *spec) warn: WARNING("Invalid format specifier: %s", *_fmt); end: - *_fmt = ++fmt; + if (r) + *_fmt = ++fmt; + return r; } static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value arg) @@ -128,7 +139,7 @@ static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value string_append_cstr(s, buf, len); break; case FMT_STRING: - string_append(s, heap[arg.i].s); + string_append(s, heap_get_string(arg.i)); heap_unref(arg.i); break; case FMT_CHAR: @@ -143,15 +154,17 @@ static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value } } -struct string *string_format(struct string *fmt, union vm_value arg) +struct string *string_format(struct string *fmt, union vm_value arg, enum ain_data_type type) { struct string *out = string_ref(&EMPTY_STRING); for (const char *s = fmt->text; *s; s++) { - if (*s == '%') { - struct fmt_spec spec; - string_append_cstr(&out, fmt->text, s - fmt->text); - s++; - parse_fmt_spec(&s, &spec); + if (*s != '%') + continue; + + size_t size = s - fmt->text; + struct fmt_spec spec; + if (parse_fmt_spec(&s, &spec, type)) { + string_append_cstr(&out, fmt->text, size); append_fmt(&out, &spec, arg); string_append_cstr(&out, s, strlen(s)); break; diff --git a/src/parts/parts.c b/src/parts/parts.c index 3e7e5eb..de9b7a7 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -960,7 +960,7 @@ bool PE_SetLoopCG_by_index(int parts_no, int cg_no, int nr_frames, int frame_tim static struct cg *load_loop_cg_by_name(int no, void *data) { int unused_no; - struct string *cg_name = string_format((struct string*)data, (union vm_value){.i=no}); + struct string *cg_name = string_format((struct string*)data, (union vm_value){.i=no}, AIN_INT); struct cg *cg = asset_cg_load_by_name(cg_name->text, &unused_no); free_string(cg_name); return cg; diff --git a/src/vm.c b/src/vm.c index 94e2619..78131ef 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1499,11 +1499,11 @@ static enum opcode execute_instruction(enum opcode opcode) break; } case S_MOD: { - stack_pop(); // ??? + int type = stack_pop().i; union vm_value val = stack_pop(); int fmt = stack_pop().i; int dst = heap_alloc_slot(VM_STRING); - heap[dst].s = string_format(heap[fmt].s, val); + heap[dst].s = string_format(heap[fmt].s, val, type + 8); heap_unref(fmt); stack_push(dst); break; diff --git a/subprojects/libsys4 b/subprojects/libsys4 index 863bd9d..95ce167 160000 --- a/subprojects/libsys4 +++ b/subprojects/libsys4 @@ -1 +1 @@ -Subproject commit 863bd9d332d74fc843b0b5f8b26f774db1ab8d7b +Subproject commit 95ce167a93fcc3348431583d2f7f0b3637b12f79 diff --git a/test/Source/strings.jaf b/test/Source/strings.jaf index b7aef4e..98c2904 100755 --- a/test/Source/strings.jaf +++ b/test/Source/strings.jaf @@ -73,6 +73,7 @@ void test_strings(void) test_string("abc%4Ddef % 42", "abc%4Ddef" % 42, "abc@@‚S‚Qdef"); test_string("abc%04Ddef % 42", "abc%04Ddef" % 42, "abc‚O‚O‚S‚Qdef"); test_string("\"%d %d\" % 42 % 43", "%d %d" % 42 % 43, "42 43"); + test_string("%s %d % 42", "%s %d" % 42, "%s 42"); // float test_string("abc%fdef % 12.3", "abc%fdef" % 12.3, "abc12.300000def"); test_string("abc%.2fdef % 12.3", "abc%.2fdef" % 12.3, "abc12.30def");