From ddeb452be530db504accedb34111272864bc9380 Mon Sep 17 00:00:00 2001 From: LucasChen <6caschen@gmail.com> Date: Tue, 7 Apr 2026 18:06:54 +0800 Subject: [PATCH 1/5] feat: Implement EV3 runtime standard and parse_int --- devices/wasm/wasm/lib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devices/wasm/wasm/lib.c b/devices/wasm/wasm/lib.c index a3f541e..00605ea 100644 --- a/devices/wasm/wasm/lib.c +++ b/devices/wasm/wasm/lib.c @@ -62,6 +62,11 @@ static void print_flush(bool is_error) { fprintf(is_error ? stderr : stdout, "\n"); } +/** Browser only: EV3/desktop use runner.c’s get_time_ms + clock_gettime. */ +static uint64_t wasm_get_time_ms(void) { + return (uint64_t)emscripten_get_now(); +} + EMSCRIPTEN_KEEPALIVE void siwasm_alloc_heap(size_t size) { if (heap) { @@ -93,6 +98,7 @@ void siwasm_run(unsigned char *code, size_t code_size) { sinter_printer_integer = print_integer; sinter_printer_string = print_string; sinter_printer_flush = print_flush; + sinter_get_time_ms = wasm_get_time_ms; sinter_value_t result; sinter_fault_t fault = (uint8_t) sinter_run(code, code_size, &result); From 2ea3bf1d3066692550769ab58be00bedd5c3cbfb Mon Sep 17 00:00:00 2001 From: LucasChen <6caschen@gmail.com> Date: Tue, 7 Apr 2026 18:09:44 +0800 Subject: [PATCH 2/5] feat: Implement EV3 runtime standard and parse_int --- runner/src/runner.c | 11 ++++++++ vm/include/sinter.h | 2 ++ vm/src/primitives.c | 62 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/runner/src/runner.c b/runner/src/runner.c index f5a94de..06406d1 100644 --- a/runner/src/runner.c +++ b/runner/src/runner.c @@ -8,10 +8,17 @@ #include #include +#include #include #define eprintf(...) fprintf(stderr, __VA_ARGS__) +static uint64_t get_time_ms(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t) ts.tv_sec * 1000u + (uint64_t) ts.tv_nsec / 1000000u; +} + static const char *fault_names[] = { "no fault", "out of memory", @@ -94,9 +101,13 @@ int main(int argc, char *argv[]) { sinter_printer_string = print_string; sinter_printer_integer = print_integer; sinter_printer_flush = print_flush; + sinter_get_time_ms = get_time_ms; setup_internals(); + void *heap = malloc(0x100000); // 1MB + sinter_setup_heap(heap, 0x100000); + sinter_value_t result = { 0 }; sinter_fault_t fault = sinter_run(program, size, &result); diff --git a/vm/include/sinter.h b/vm/include/sinter.h index a5f145c..767d2be 100644 --- a/vm/include/sinter.h +++ b/vm/include/sinter.h @@ -105,6 +105,8 @@ extern sinter_printfn_integer sinter_printer_integer; extern sinter_printfn_float sinter_printer_float; extern sinter_printfn_flush sinter_printer_flush; +extern uint64_t (*sinter_get_time_ms)(void); + #ifdef __cplusplus } #endif diff --git a/vm/src/primitives.c b/vm/src/primitives.c index 5bbab9c..5083bf5 100644 --- a/vm/src/primitives.c +++ b/vm/src/primitives.c @@ -1596,6 +1596,64 @@ static sinanbox_t sivmfn_prim_noop(uint8_t argc, sinanbox_t *argv) { return NANBOX_OFUNDEF(); } +uint64_t (*sinter_get_time_ms)(void) = NULL; + +static sinanbox_t sivmfn_prim_parse_int(uint8_t argc, sinanbox_t *argv) { + CHECK_ARGC(2); + + if (!NANBOX_ISPTR(argv[0]) || !siheap_is_string(SIHEAP_NANBOXTOPTR(argv[0]))) { + sifault(sinter_fault_type); + return NANBOX_OFEMPTY(); + } + + int32_t radix = 0; + if (NANBOX_ISINT(argv[1])) { + radix = NANBOX_INT(argv[1]); + } else if (NANBOX_ISFLOAT(argv[1])) { + float fv = NANBOX_FLOAT(argv[1]); + if (!isfinite(fv) || truncf(fv) != fv) { + sifault(sinter_fault_type); + return NANBOX_OFEMPTY(); + } + radix = (int32_t) fv; + } else { + sifault(sinter_fault_type); + return NANBOX_OFEMPTY(); + } + + if (radix < 2 || radix > 36) { + sifault(sinter_fault_type); + return NANBOX_OFEMPTY(); + } + + const char *str = sistrobj_tocharptr(SIHEAP_NANBOXTOPTR(argv[0])); + char *endptr = NULL; + long result = strtol(str, &endptr, radix); + + if (endptr == str) { + return NANBOX_CANONICAL_NAN; + } + + if (result >= NANBOX_INTMIN && result <= NANBOX_INTMAX) { + return NANBOX_OFINT((int32_t) result); + } else { + return NANBOX_OFFLOAT((float) result); + } +} + +static sinanbox_t sivmfn_prim_runtime(uint8_t argc, sinanbox_t *argv) { + (void) argc; (void) argv; + if (sinter_get_time_ms) { + uint64_t t = sinter_get_time_ms(); + if (t <= (uint64_t) NANBOX_INTMAX) { + return NANBOX_OFINT((int32_t) t); + } + return NANBOX_OFFLOAT((float) t); + } + return NANBOX_OFINT(0); +} + + sivmfnptr_t sivmfn_primitives[] = { sivmfn_prim_accumulate, sivmfn_prim_append, @@ -1666,11 +1724,11 @@ sivmfnptr_t sivmfn_primitives[] = { sivmfn_prim_math_trunc, sivmfn_prim_member, sivmfn_prim_pair, - /* parse_int */ sivmfn_prim_unimpl, // TODO: doesn't make sense without the ability to take input (prompt) + /* parse_int */ sivmfn_prim_parse_int, sivmfn_prim_remove, sivmfn_prim_remove_all, sivmfn_prim_reverse, - /* runtime */ sivmfn_prim_unimpl, // TODO: need to get time from host + /* runtime */ sivmfn_prim_runtime, sivmfn_prim_set_head, sivmfn_prim_set_tail, sivmfn_prim_stream, From c7f17ba0972e6df7a8966dda306f38e1f89527e2 Mon Sep 17 00:00:00 2001 From: LucasChen <6caschen@gmail.com> Date: Tue, 7 Apr 2026 18:52:29 +0800 Subject: [PATCH 3/5] fix: null check malloc and handle strtol ERANGE --- runner/src/runner.c | 4 ++++ vm/src/primitives.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/runner/src/runner.c b/runner/src/runner.c index 06406d1..b8dbf2a 100644 --- a/runner/src/runner.c +++ b/runner/src/runner.c @@ -106,6 +106,10 @@ int main(int argc, char *argv[]) { setup_internals(); void *heap = malloc(0x100000); // 1MB + if (!heap) { + eprintf("Failed to allocate heap\n"); + return 1; + } sinter_setup_heap(heap, 0x100000); sinter_value_t result = { 0 }; diff --git a/vm/src/primitives.c b/vm/src/primitives.c index 5083bf5..724fc58 100644 --- a/vm/src/primitives.c +++ b/vm/src/primitives.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -1628,12 +1629,18 @@ static sinanbox_t sivmfn_prim_parse_int(uint8_t argc, sinanbox_t *argv) { const char *str = sistrobj_tocharptr(SIHEAP_NANBOXTOPTR(argv[0])); char *endptr = NULL; + errno = 0; long result = strtol(str, &endptr, radix); if (endptr == str) { return NANBOX_CANONICAL_NAN; } + if (errno == ERANGE) { + // strtol overflowed, return as float instead + return NANBOX_OFFLOAT((float) result); + } + if (result >= NANBOX_INTMIN && result <= NANBOX_INTMAX) { return NANBOX_OFINT((int32_t) result); } else { From d7a03a3865e0de99652770dbd479cf0e5457d9a9 Mon Sep 17 00:00:00 2001 From: LucasChen <6caschen@gmail.com> Date: Tue, 7 Apr 2026 20:35:02 +0800 Subject: [PATCH 4/5] fix: define _POSIX_C_SOURCE for clock_gettime on Linux --- runner/src/runner.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runner/src/runner.c b/runner/src/runner.c index b8dbf2a..e3cbba1 100644 --- a/runner/src/runner.c +++ b/runner/src/runner.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L + #include #include #include From 9c507580cf43acf9b2f4c2e792c45cf1ccfdad3c Mon Sep 17 00:00:00 2001 From: LucasChen <6caschen@gmail.com> Date: Thu, 9 Apr 2026 10:33:26 +0800 Subject: [PATCH 5/5] fix: solve web-demo cannot build issue --- devices/wasm/web/preact.config.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/devices/wasm/web/preact.config.js b/devices/wasm/web/preact.config.js index e3ecc99..0b3bdda 100644 --- a/devices/wasm/web/preact.config.js +++ b/devices/wasm/web/preact.config.js @@ -19,6 +19,19 @@ export default (config, env, helpers, options) => { config.node.Buffer = true; config.node.process = true; + // Webpack 4 doesn't recognise the `node:` prefix for built-in modules. + // Alias them to the bare specifiers so webpack can polyfill/stub them. + config.resolve.alias = Object.assign(config.resolve.alias || {}, { + "node:fs": "fs", + "node:path": "path", + "node:os": "os", + "node:crypto": "crypto", + "node:buffer": "buffer", + "node:stream": "stream", + "node:util": "util", + "node:child_process": "child_process", + }); + if (env.isProd) { config.output.publicPath = ""; }