From d9dc57abfc6ce54cb8ddce35cfbb145f30330c88 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 22:45:26 +0100 Subject: [PATCH 01/51] test: epc self-hosting parity scoreboard (baseline 13/54 runnable, 5/9 rejections) tests/run_epc_parity.sh compiles every runnable tests/*.ep with the SELF-HOSTED compiler (./epc), runs the binary, and diffs stdout against .expected. Expected- compile-error tests form a rejection section (epc must reject them once the self-hosted semantic passes land). Baseline measured on this commit: PASS 13/54 runnable; 5 rejected / 4 wrongly accepted. Also handles epc's CWD output-path divergence (tracked for Phase 4) and cleans up generated artifacts. .gitignore gains !tests/*.sh. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 1 + tests/run_epc_parity.sh | 98 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100755 tests/run_epc_parity.sh diff --git a/.gitignore b/.gitignore index 4e4b176..a6e8cf4 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,4 @@ tests/* !tests/*.ep !tests/*.expected !tests/*.md +!tests/*.sh diff --git a/tests/run_epc_parity.sh b/tests/run_epc_parity.sh new file mode 100755 index 0000000..76bd670 --- /dev/null +++ b/tests/run_epc_parity.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# +# run_epc_parity.sh — the self-hosting parity scoreboard. +# +# For every runnable test (tests/test_*.ep without '# expected_compile_error'): +# 1. compile it with the SELF-HOSTED compiler (./epc) +# 2. run the binary (exit 0 required) +# 3. if tests/.expected exists, stdout must match exactly +# +# Rejection section: every '# expected_compile_error' test must FAIL to compile +# with ./epc once the self-hosted semantic passes land (until then they are +# reported, not counted, so the scoreboard stays honest about what epc enforces). +# +# Usage: tests/run_epc_parity.sh [--verbose] +# Exit code: 0 only if every runnable test passes AND every rejection test rejects. +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$SCRIPT_DIR" + +VERBOSE=0 +[[ "${1:-}" == "--verbose" ]] && VERBOSE=1 + +EPC=./epc +if [[ ! -x "$EPC" ]]; then + echo "error: $EPC not found — build it first: ./target/release/ernos epc.ep" >&2 + exit 2 +fi + +PASS=0; FAIL=0; FAILED="" +REJ_OK=0; REJ_MISS=0; REJ_MISSED="" + +for TEST_FILE in tests/test_*.ep; do + [[ -f "$TEST_FILE" ]] || continue + NAME=$(basename "$TEST_FILE" .ep) + BINARY="tests/$NAME" + + if head -5 "$TEST_FILE" | grep -q '# expected_compile_error'; then + # ── Rejection section ── + if "$EPC" "$TEST_FILE" >/dev/null 2>&1; then + REJ_MISS=$((REJ_MISS+1)); REJ_MISSED="$REJ_MISSED $NAME" + [[ $VERBOSE -eq 1 ]] && echo "REJECT-MISS $NAME (epc accepted a program it must reject)" + else + REJ_OK=$((REJ_OK+1)) + [[ $VERBOSE -eq 1 ]] && echo "REJECT-OK $NAME" + fi + continue + fi + + # ── Runnable section ── + if ! COMPILE_OUT=$("$EPC" "$TEST_FILE" 2>&1); then + FAIL=$((FAIL+1)); FAILED="$FAILED $NAME(compile)" + [[ $VERBOSE -eq 1 ]] && { echo "FAIL $NAME (compile)"; echo "$COMPILE_OUT" | grep -iE "error" | head -2; } + continue + fi + # epc currently writes the binary into CWD (basename); ernos writes next to + # the source. Accept either so the scoreboard measures compilation, not the + # output-path divergence (tracked separately in Phase 4). + if [[ ! -x "$BINARY" && -x "./$NAME" ]]; then + BINARY="./$NAME" + fi + if [[ ! -x "$BINARY" ]]; then + FAIL=$((FAIL+1)); FAILED="$FAILED $NAME(no-binary)" + [[ $VERBOSE -eq 1 ]] && echo "FAIL $NAME (compiled but no binary found)" + continue + fi + RUN_OUT=$("$BINARY" 2>/dev/null); RC=$? + if [[ $RC -ne 0 ]]; then + FAIL=$((FAIL+1)); FAILED="$FAILED $NAME(exit=$RC)" + [[ $VERBOSE -eq 1 ]] && echo "FAIL $NAME (exit $RC)" + continue + fi + EXPECTED_FILE="tests/$NAME.expected" + if [[ -f "$EXPECTED_FILE" ]] && ! diff -q <(printf '%s\n' "$RUN_OUT") "$EXPECTED_FILE" >/dev/null 2>&1; then + FAIL=$((FAIL+1)); FAILED="$FAILED $NAME(output)" + [[ $VERBOSE -eq 1 ]] && { echo "FAIL $NAME (output mismatch)"; diff <(printf '%s\n' "$RUN_OUT") "$EXPECTED_FILE" | head -6; } + continue + fi + PASS=$((PASS+1)) + [[ $VERBOSE -eq 1 ]] && echo "PASS $NAME" +done + +# Clean up CWD-dropped binaries and generated C from the epc runs. +for TEST_FILE in tests/test_*.ep; do + NAME=$(basename "$TEST_FILE" .ep) + [[ -f "./$NAME" && -x "./$NAME" ]] && rm -f "./$NAME" + rm -f "./${NAME}_compiled.c" +done + +TOTAL=$((PASS+FAIL)) +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " epc parity: PASS $PASS/$TOTAL runnable" +[[ -n "$FAILED" ]] && echo " failing:$FAILED" | tr ' ' '\n' | sed 's/^/ /' | sed '1s/ failing:/ failing:/' +echo " rejection: $REJ_OK rejected, $REJ_MISS wrongly accepted" +[[ -n "$REJ_MISSED" ]] && echo " wrongly accepted:$REJ_MISSED" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +[[ $FAIL -eq 0 && $REJ_MISS -eq 0 ]] && exit 0 || exit 1 From 1b8d50904f6e9cda945adbde548bce65c106038d Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 22:56:25 +0100 Subject: [PATCH 02/51] refactor: extract C runtime to runtime/ep_runtime.c; make emitted C deterministic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RUNTIME_HEADER_AND_SRC (4708 lines of C) moves to runtime/ep_runtime.c, embedded via include_str! — byte-identical by construction. This is the single-source-of-truth runtime both compilers will embed (self-hosted side lands next). - Fix nondeterministic emission: six codegen loops iterated a HashMap when declaring locals / pushing GC roots, so the SAME compiler binary produced different C run-to-run (verified: 3 distinct hashes in 4 runs). Locals are now emitted in sorted order; 4/4 emissions hash-identical after the fix. Prerequisite for reproducible builds and the ernos==epc byte-parity gate. run_tests.sh 69/69; self-hosting gate passes. Co-Authored-By: Claude Opus 4.8 (1M context) --- runtime/ep_runtime.c | 4708 +++++++++++++++++++++++++++++++++++++++++ src/codegen.rs | 4740 +----------------------------------------- 2 files changed, 4733 insertions(+), 4715 deletions(-) create mode 100644 runtime/ep_runtime.c diff --git a/runtime/ep_runtime.c b/runtime/ep_runtime.c new file mode 100644 index 0000000..da41f56 --- /dev/null +++ b/runtime/ep_runtime.c @@ -0,0 +1,4708 @@ +#include +#include +#include +#include +#ifdef __wasm__ +#define _SETJMP_H +typedef int jmp_buf[1]; +#define setjmp(buf) (0) +#define longjmp(buf, val) abort() + +// Mock pthreads for single-threaded WASM +typedef struct { int lock_state; } pthread_mutex_t; +typedef struct { int cond_state; } pthread_cond_t; +typedef struct { int rw_state; } pthread_rwlock_t; +typedef int pthread_t; +typedef int pthread_attr_t; +#define PTHREAD_MUTEX_INITIALIZER {0} +#define PTHREAD_COND_INITIALIZER {0} +#define PTHREAD_RWLOCK_INITIALIZER {0} +#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0) +#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0) +#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0) +#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1) +#define pthread_mutex_destroy(m) ((void)(m), 0) +#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0) +#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0) +#define pthread_cond_signal(c) ((void)(c), 0) +#define pthread_cond_broadcast(c) ((void)(c), 0) +#define pthread_cond_destroy(c) ((void)(c), 0) +#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0) +#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0) +#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0) +#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0) +#define pthread_rwlock_destroy(r) ((void)(r), 0) +#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0) +#define pthread_join(t, r) ((void)(t), (void)(r), 0) +#define pthread_detach(t) ((void)(t), 0) +#else +#include +#endif +#include +#include +#ifndef _WIN32 +#include +#endif +#if defined(__APPLE__) +#include +#endif +#if defined(__linux__) +#include +#endif +#include + +/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on + Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a + /dev/urandom read elsewhere. Only if all of those are unavailable does it + fall back to rand() — never on a supported platform. */ +static void ep_secure_random_bytes(unsigned char* buf, size_t n) { +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + arc4random_buf(buf, n); +#else + size_t got = 0; + #if defined(__linux__) + while (got < n) { + ssize_t r = getrandom(buf + got, n - got, 0); + if (r <= 0) break; + got += (size_t)r; + } + #endif + if (got < n) { + FILE* f = fopen("/dev/urandom", "rb"); + if (f) { + got += fread(buf + got, 1, n - got, f); + fclose(f); + } + } + while (got < n) { + buf[got++] = (unsigned char)(rand() & 0xFF); + } +#endif +} + +/* Try/catch infrastructure */ +static jmp_buf ep_try_buf; +static volatile int ep_try_active = 0; + +static void ep_signal_handler(int sig) { + if (ep_try_active) { + ep_try_active = 0; + longjmp(ep_try_buf, sig); + } + /* Outside try: print error and exit */ + const char* name = sig == SIGSEGV ? "segmentation fault (null pointer or invalid memory access)" + : sig == SIGFPE ? "arithmetic error (division by zero)" + : sig == SIGABRT ? "aborted" + : "unknown signal"; + fprintf(stderr, "\nRuntime Error: %s (signal %d)\n", name, sig); + + /* Write to daemon/general log file if environment variable is set */ + const char* daemon_log = getenv("ERNOS_DAEMON_LOG"); + if (!daemon_log || daemon_log[0] == '\0') { + daemon_log = getenv("ERNOS_LOG_FILE"); + } + if (daemon_log && daemon_log[0] != '\0') { + FILE* f = fopen(daemon_log, "ab"); + if (f) { + time_t rawtime; + time(&rawtime); + struct tm * timeinfo = localtime(&rawtime); + char time_buf[80]; + if (timeinfo) { + strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", timeinfo); + } else { + snprintf(time_buf, sizeof(time_buf), "%lld", (long long)rawtime); + } + fprintf(f, "[%s] FATAL: Runtime Error: %s (signal %d)\n", time_buf, name, sig); + fclose(f); + } + } + + _exit(128 + sig); +} + +#ifdef _MSC_VER +static void ep_install_signal_handlers(void); +#pragma section(".CRT$XCU", read) +__declspec(allocate(".CRT$XCU")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers; +static void ep_install_signal_handlers(void) { +#else +__attribute__((constructor)) +static void ep_install_signal_handlers(void) { +#endif + signal(SIGFPE, ep_signal_handler); + signal(SIGSEGV, ep_signal_handler); + signal(SIGABRT, ep_signal_handler); +#ifdef _WIN32 + { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); } +#endif +} + +#if defined(__wasm__) + typedef int ep_thread_t; + typedef int ep_mutex_t; + typedef int ep_cond_t; + #define ep_mutex_init(m) (void)(0) + #define ep_mutex_lock(m) (void)(0) + #define ep_mutex_unlock(m) (void)(0) + #define ep_cond_init(c) (void)(0) + #define ep_cond_wait(c, m) (void)(0) + #define ep_cond_signal(c) (void)(0) +#elif defined(_WIN32) + #include + #include + #include + #pragma comment(lib, "ws2_32.lib") + typedef HANDLE ep_thread_t; + typedef CRITICAL_SECTION ep_mutex_t; + typedef CONDITION_VARIABLE ep_cond_t; + #define ep_mutex_init(m) InitializeCriticalSection(m) + #define ep_mutex_lock(m) EnterCriticalSection(m) + #define ep_mutex_unlock(m) LeaveCriticalSection(m) + #define ep_cond_init(c) InitializeConditionVariable(c) + #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE) + #define ep_cond_signal(c) WakeConditionVariable(c) +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + typedef pthread_t ep_thread_t; + typedef pthread_mutex_t ep_mutex_t; + typedef pthread_cond_t ep_cond_t; + #define ep_mutex_init(m) pthread_mutex_init(m, NULL) + #define ep_mutex_lock(m) pthread_mutex_lock(m) + #define ep_mutex_unlock(m) pthread_mutex_unlock(m) + #define ep_cond_init(c) pthread_cond_init(c, NULL) + #define ep_cond_wait(c, m) pthread_cond_wait(c, m) + #define ep_cond_signal(c) pthread_cond_signal(c) +#endif + +/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */ + +#include +#if !defined(__wasm__) && !defined(_WIN32) +#include +#endif + +typedef enum { + EP_OBJ_LIST, + EP_OBJ_STRING, + EP_OBJ_STRUCT, + EP_OBJ_CLOSURE, + EP_OBJ_MAP +} EpObjKind; + +typedef struct EpGCObject { + EpObjKind kind; + int marked; + void* ptr; /* actual allocation pointer */ + long long size; /* payload size for structs */ + long long num_fields; /* number of fields for structs (each is long long) */ + int generation; /* 0 = Nursery/young, 1 = Old */ + struct EpGCObject* next; /* intrusive linked list */ +} EpGCObject; + +long long ep_time_now_ms(void); +long long ep_sleep_ms(long long ms); + +typedef struct EpTask EpTask; +typedef struct { + long long chan; + int completed; + long long value; + EpTask* waiting_task; +} EpFuture; + +static long long ep_await_future(EpFuture* fut); + +struct EpTask { + long long (*step)(void*); /* pointer to step function */ + void* args; /* pointer to step state arguments */ + long long args_size_bytes; /* size of args struct for GC tracing */ + EpTask* next; /* run-queue link pointer */ + EpFuture* fut; /* future associated with this task */ + int state; /* coroutine execution state */ + int is_cancelled; /* cancellation flag for structured concurrency */ + struct EpTask* parent; /* parent task for structured concurrency cancellation */ +}; + +/* Event Loop Scheduler Globals & Functions */ +static EpTask* ep_run_queue_head = NULL; +static EpTask* ep_run_queue_tail = NULL; +static EpTask* ep_current_task = NULL; +static int ep_event_loop_fd = -1; /* epoll or kqueue fd */ +static int ep_active_io_sources = 0; + +static void ep_task_enqueue(EpTask* task) { + if (!task) return; + task->next = NULL; + if (ep_run_queue_tail) { + ep_run_queue_tail->next = task; + ep_run_queue_tail = task; + } else { + ep_run_queue_head = ep_run_queue_tail = task; + } +} + +static EpTask* ep_task_dequeue(void) { + if (!ep_run_queue_head) return NULL; + EpTask* task = ep_run_queue_head; + ep_run_queue_head = ep_run_queue_head->next; + if (!ep_run_queue_head) ep_run_queue_tail = NULL; + return task; +} + +#ifndef __wasm__ +#ifdef __APPLE__ +#include +#else +#include +#endif +#endif + +static void ep_async_loop_init(void) { + if (ep_event_loop_fd != -1) return; +#ifdef __wasm__ + ep_event_loop_fd = 999; +#elif defined(__APPLE__) + ep_event_loop_fd = kqueue(); +#else + ep_event_loop_fd = epoll_create1(0); +#endif +} + +typedef struct EpTimer { + long long expiry_ms; + EpTask* task; + struct EpTimer* next; +} EpTimer; +static EpTimer* ep_timers_head = NULL; + +static void ep_async_register_timer(long long timeout_ms, EpTask* task) { + long long expiry = ep_time_now_ms() + timeout_ms; + EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer)); + timer->expiry_ms = expiry; + timer->task = task; + timer->next = NULL; + + /* Insert sorted */ + if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) { + timer->next = ep_timers_head; + ep_timers_head = timer; + } else { + EpTimer* cur = ep_timers_head; + while (cur->next && cur->next->expiry_ms <= expiry) { + cur = cur->next; + } + timer->next = cur->next; + cur->next = timer; + } +} + +static long long ep_get_next_timer_timeout(void) { + if (!ep_timers_head) return -1; /* block indefinitely */ + long long now = ep_time_now_ms(); + long long diff = ep_timers_head->expiry_ms - now; + return diff < 0 ? 0 : diff; +} + +static void ep_process_expired_timers(void) { + long long now = ep_time_now_ms(); + while (ep_timers_head && ep_timers_head->expiry_ms <= now) { + EpTimer* expired = ep_timers_head; + ep_timers_head = ep_timers_head->next; + ep_task_enqueue(expired->task); + free(expired); + } +} + +static void ep_async_register_read(int fd, EpTask* task) { +#ifdef __wasm__ + (void)fd; + (void)task; +#else + ep_async_loop_init(); + ep_active_io_sources++; +#ifdef __APPLE__ + struct kevent ev; + EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task); + kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL); +#else + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLONESHOT; + ev.data.ptr = task; + if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev); + } +#endif +#endif +} + +static void ep_async_wait_step(long long timeout) { +#ifdef __wasm__ + if (timeout > 0) { + ep_sleep_ms(timeout); + } +#else +#ifdef __APPLE__ + struct kevent events[16]; + struct timespec ts; + struct timespec* p_ts = NULL; + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + p_ts = &ts; + } + int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts); + for (int i = 0; i < n; i++) { + EpTask* t = (EpTask*)events[i].udata; + ep_task_enqueue(t); + ep_active_io_sources--; + } +#else + struct epoll_event events[16]; + int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout); + for (int i = 0; i < n; i++) { + EpTask* t = (EpTask*)events[i].data.ptr; + ep_task_enqueue(t); + ep_active_io_sources--; + } +#endif +#endif + ep_process_expired_timers(); +} + +static void ep_async_loop_run(void) { + ep_async_loop_init(); + while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) { + /* 1. Run all runnable tasks */ + while (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + continue; + } + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = NULL; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + + /* 2. If no tasks runnable, wait for I/O / timers */ + if (!ep_run_queue_head) { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + break; + } + + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + continue; + } + + ep_async_wait_step(timeout); + } + } +} + +static long long ep_await_future(EpFuture* fut) { + if (!fut) return 0; + while (!fut->completed) { + if (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task) { + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + } else { + EpTask* saved_current = ep_current_task; + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = saved_current; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + } + } else { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + fprintf(stderr, "Deadlock detected: awaiting incomplete future with no active tasks or timers.\n"); + exit(1); + } + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + } else { + ep_async_wait_step(timeout); + } + } + } + return fut->value; +} + +static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind); +long long create_list(void); +long long append_list(long long list_ptr, long long value); + +typedef struct { + EpFuture* futures[128]; + int count; + int has_error; +} EpTaskGroup; + +typedef struct { + EpFuture* fut; + int timer_fired; +} EpTimeoutArgs; + +static EpTask* ep_find_task_by_future(EpFuture* fut) { + if (!fut) return NULL; + EpTask* cur = ep_run_queue_head; + while (cur) { + if (cur->fut == fut) return cur; + cur = cur->next; + } + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task && timer->task->fut == fut) return timer->task; + timer = timer->next; + } + return NULL; +} + +static void ep_cancel_task(EpTask* task) { + if (!task) return; + task->is_cancelled = 1; + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + // Cancel children in run queue + EpTask* cur = ep_run_queue_head; + while (cur) { + if (cur->parent == task) { + ep_cancel_task(cur); + } + cur = cur->next; + } + // Cancel children in timers queue + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task && timer->task->parent == task) { + ep_cancel_task(timer->task); + } + timer = timer->next; + } +} + +static long long create_task_group(void) { + EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup)); + tg->count = 0; + tg->has_error = 0; + { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; } + return (long long)tg; +} + +static long long add_task_group(long long group_ptr, long long fut_ptr) { + EpTaskGroup* tg = (EpTaskGroup*)group_ptr; + EpFuture* fut = (EpFuture*)fut_ptr; + if (!tg || !fut) return 0; + if (tg->count < 128) { + tg->futures[tg->count++] = fut; + // Associate the task's parent with the current task so it's cancellation-linked + EpTask* task = ep_find_task_by_future(fut); + if (task) { + task->parent = ep_current_task; + } + } + return 0; +} + +static long long wait_task_group(long long group_ptr) { + EpTaskGroup* tg = (EpTaskGroup*)group_ptr; + if (!tg) return 0; + + int all_done = 0; + while (!all_done) { + all_done = 1; + for (int i = 0; i < tg->count; i++) { + EpFuture* fut = tg->futures[i]; + if (!fut->completed) { + all_done = 0; + break; + } + } + + if (all_done) break; + + if (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task) { + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + } else { + EpTask* saved_current = ep_current_task; + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = saved_current; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + } + } else { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n"); + exit(1); + } + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + } else { + ep_async_wait_step(timeout); + } + } + + // Propagate cancellation/failure inside task group + for (int i = 0; i < tg->count; i++) { + EpFuture* fut = tg->futures[i]; + if (fut->completed && fut->value == -1) { + tg->has_error = 1; + for (int j = 0; j < tg->count; j++) { + EpFuture* other_fut = tg->futures[j]; + if (!other_fut->completed) { + EpTask* other_task = ep_find_task_by_future(other_fut); + if (other_task) { + ep_cancel_task(other_task); + } else { + other_fut->completed = 1; + other_fut->value = -1; + } + } + } + } + } + } + + long long list = create_list(); + for (int i = 0; i < tg->count; i++) { + append_list(list, tg->futures[i]->value); + } + return list; +} + +static long long ep_timeout_timer_step(void* r) { + EpTimeoutArgs* args = (EpTimeoutArgs*)r; + if (args && args->fut && !args->fut->completed) { + args->timer_fired = 1; + EpTask* task = ep_find_task_by_future(args->fut); + if (task) { + ep_cancel_task(task); + } else { + args->fut->completed = 1; + args->fut->value = -1; + } + } + return 0; +} + +static long long async_timeout(long long timeout_ms, long long fut_ptr) { + EpFuture* fut = (EpFuture*)fut_ptr; + if (!fut) return -1; + if (fut->completed) return fut->value; + + EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs)); + args->fut = fut; + args->timer_fired = 0; + + EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask)); + timer_task->step = ep_timeout_timer_step; + timer_task->args = args; + timer_task->args_size_bytes = sizeof(EpTimeoutArgs); + timer_task->fut = NULL; + timer_task->state = 0; + timer_task->is_cancelled = 0; + timer_task->parent = NULL; + + ep_async_register_timer(timeout_ms, timer_task); + + while (!fut->completed && !(args->timer_fired)) { + if (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task) { + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + } else { + EpTask* saved_current = ep_current_task; + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = saved_current; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + } + } else { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + break; + } + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + } else { + ep_async_wait_step(timeout); + } + } + } + + return fut->value; +} + +/* ── Awaitable async socket-readability ───────────────────────────────────── + `await async_wait_readable(fd)` suspends the calling async task until `fd` is + readable, letting the event loop run other tasks (e.g. another agent waiting on + its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a + oneshot read-readiness task with the loop, return the future. When fd becomes + readable, ep_async_wait_step re-enqueues the task; its step completes the future + and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently + on ONE thread — no OS threads, no shared-heap GC race. */ +typedef struct { EpFuture* fut; } EpReadReadyArgs; +static long long ep_read_ready_step(void* r) { + EpReadReadyArgs* args = (EpReadReadyArgs*)r; + if (args && args->fut) { + args->fut->completed = 1; + args->fut->value = 1; + if (args->fut->waiting_task) { + ep_task_enqueue(args->fut->waiting_task); + args->fut->waiting_task = NULL; + } + } + return 0; +} +long long async_wait_readable(long long fd) { + EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture)); + fut->completed = 0; + fut->value = 0; + fut->waiting_task = NULL; + fut->chan = 0; + { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; } + EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs)); + args->fut = fut; + EpTask* task = (EpTask*)malloc(sizeof(EpTask)); + task->step = ep_read_ready_step; + task->args = args; + task->args_size_bytes = sizeof(EpReadReadyArgs); + task->fut = NULL; + task->state = 0; + task->is_cancelled = 0; + task->parent = ep_current_task; + ep_async_register_read((int)fd, task); + return (long long)fut; +} + +typedef struct { + EpFuture* fut; +} EpSleepTimerArgs; + +static long long ep_sleep_timer_step(void* r) { + EpSleepTimerArgs* args = (EpSleepTimerArgs*)r; + if (args && args->fut) { + args->fut->completed = 1; + args->fut->value = 0; + if (args->fut->waiting_task) { + ep_task_enqueue(args->fut->waiting_task); + args->fut->waiting_task = NULL; + } + } + return 0; +} + +static long long sleep_ms(long long ms) { + EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture)); + fut->completed = 0; + fut->value = 0; + fut->waiting_task = NULL; + fut->chan = 0; + { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; } + + EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs)); + args->fut = fut; + + EpTask* task = (EpTask*)malloc(sizeof(EpTask)); + task->step = ep_sleep_timer_step; + task->args = args; + task->args_size_bytes = sizeof(EpSleepTimerArgs); + task->fut = NULL; + task->state = 0; + task->is_cancelled = 0; + task->parent = ep_current_task; + + ep_async_register_timer(ms, task); + return (long long)fut; +} + +static long long cancel_task(long long fut_ptr) { + EpFuture* fut = (EpFuture*)fut_ptr; + if (fut) { + EpTask* task = ep_find_task_by_future(fut); + if (task) { + ep_cancel_task(task); + } else { + fut->completed = 1; + fut->value = -1; + } + } + return 0; +} + +/* Closure environment — captures travel with the function pointer */ +#define EP_CLOSURE_MAGIC 0x4550434C4FL +typedef struct { + long long magic; + long long fn_ptr; + long long env[]; /* flexible array of captured values */ +} EpClosure; + +/* GC globals */ +static EpGCObject* ep_gc_head = NULL; +static long long ep_gc_count = 0; +static long long ep_gc_threshold = 4096; +static int ep_gc_enabled = 1; +static long long ep_gc_nursery_count = 0; +static long long ep_gc_nursery_threshold = 512; +static int ep_gc_minor_count = 0; +static int ep_gc_major_count = 0; +static void** ep_gc_remembered_set = NULL; +static long long ep_gc_remembered_cap = 0; +static long long ep_gc_remembered_size = 0; +/* Single mutex for ALL GC and thread registry operations. + Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex) + which caused deadlock under concurrent channel load: thread A held gc_mutex + and waited for registry_mutex, thread B held registry_mutex and waited for + gc_mutex. Single lock eliminates the ordering problem. */ +#ifdef __wasm__ +#define __thread +#endif +static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in + ep_gc_stop_the_world(), waits until every *other* registered thread has parked + at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs + concurrently with a mutator changing its roots or an object's fields — the + "marking races with running mutators" hazard. All three fields are touched + only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at + safepoints are a benign optimization: a missed set just defers parking to the + next safepoint, and the collector's bounded wait covers it). */ +static volatile int ep_gc_stop_requested = 0; +static int ep_gc_parked_count = 0; +static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER; + +/* Function pointer for channel scanning — set after EpChannel is defined. + GC mark calls this to scan values in-transit in channel buffers. */ +static void (*ep_gc_scan_channels_major)(void) = NULL; +static void (*ep_gc_scan_channels_minor)(void) = NULL; +/* Function pointers for marking top-level constant/global variables, which are + GC roots that live outside any function frame. Set by __ep_init_constants. */ +static void (*ep_gc_mark_globals_major)(void) = NULL; +static void (*ep_gc_mark_globals_minor)(void) = NULL; +/* Function pointers for map value traversal — set after EpMap is defined. + GC mark calls these to recursively mark values stored in maps. */ +static void (*ep_gc_mark_map_values)(void* ptr) = NULL; +static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL; + +/* Thread registry for GC root scanning in multi-threaded environment */ +#define EP_MAX_THREADS 256 +static __thread void* volatile ep_thread_local_top = NULL; +static __thread void* ep_thread_local_bottom = NULL; + +static void* volatile* ep_thread_tops[EP_MAX_THREADS]; +static void* ep_thread_bottoms[EP_MAX_THREADS]; +static volatile int ep_thread_active[EP_MAX_THREADS]; +static int ep_num_threads = 0; + +/* Per-thread GC root state — heap-allocated, stable across thread lifetime. + Previous design stored raw pointers to __thread arrays (ep_gc_root_stack, + ep_gc_root_sp) in the global registry. When a thread exited, the __thread + storage was freed, leaving dangling pointers that ep_gc_mark would + dereference → segfault. Now each thread gets a heap-allocated state struct + that survives thread exit and is only recycled when the slot is reused. */ +typedef struct { + long long* roots[4096]; /* copy of root pointers, updated under lock */ + volatile int sp; /* current root stack pointer */ +} EpThreadGCState; + +static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS]; + +/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */ +#define EP_GC_MAX_ROOTS 4096 +static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS]; +static __thread int ep_gc_root_sp = 0; +static __thread int ep_thread_slot = -1; + +/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on + push and retreats on pop so that per-frame push/pop counts stay balanced. + Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further + roots are counted but not stored (those deep-overflow locals are simply not + traced) — crucially, we never overwrite or drop an outer frame's stored + roots, which the old "silently skip the push but still pop" path did. */ +static void ep_gc_push_root(long long* root) { + int idx = ep_gc_root_sp; + ep_gc_root_sp++; + if (idx < EP_GC_MAX_ROOTS) { + ep_gc_root_stack[idx] = root; + /* Update the heap-allocated state so GC mark can see it safely */ + if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) { + ep_thread_gc_states[ep_thread_slot]->roots[idx] = root; + ep_thread_gc_states[ep_thread_slot]->sp = + (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS; + } + } +} +static void ep_gc_pop_roots(long long count) { + ep_gc_root_sp -= (int)count; + if (ep_gc_root_sp < 0) ep_gc_root_sp = 0; + /* Update the heap-allocated state (clamped to the array bound) */ + if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) { + ep_thread_gc_states[ep_thread_slot]->sp = + (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS; + } +} + +/* Park the calling thread if the collector has stopped the world. + MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise + root set) is stable while parked, so the collector can scan it race-free. */ +static void ep_gc_park_if_stopped(void) { + if (!ep_gc_stop_requested) return; + /* Spill registers onto the stack and publish this thread's current stack top + so the collector can conservatively scan its frozen C stack while parked — + this catches roots held only in registers/temporaries that the precise + shadow stack does not yet record. _dummy is declared below _pregs, so its + (lower) address bounds a scan range that covers the spilled registers. */ + jmp_buf _pregs; + volatile char _top_marker; /* function-scope: stays valid while parked */ + memset(&_pregs, 0, sizeof(_pregs)); + setjmp(_pregs); + /* _top_marker is declared after _pregs, so its (lower) address bounds a scan + range [&_top_marker, stack_bottom] that covers the spilled registers. */ + ep_thread_local_top = (void*)&_top_marker; + __sync_synchronize(); /* publish shadow-stack + top writes before parking */ + ep_gc_parked_count++; + while (ep_gc_stop_requested) { + pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex); + } + ep_gc_parked_count--; +} + +/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held. + Waits (briefly releasing the lock so blocked mutators can reach a safepoint) + until all other registered threads have parked. After a bounded fallback + (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked + or idle with a stable shadow stack, so scanning it is still safe in practice. */ +static void ep_gc_stop_the_world(void) { + ep_gc_stop_requested = 1; + /* Actively-running threads reach a safepoint (every allocation and every + function entry) within microseconds, so they park on the first spin or + two. The bound only caps the rare case where a thread is blocked/idle + (e.g. just entered a channel op) and won't park — those have a stable + shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */ + for (int spins = 0; spins < 40; spins++) { + int others = 0; + for (int t = 0; t < ep_num_threads; t++) { + if (ep_thread_active[t] && t != ep_thread_slot) others++; + } + if (others <= 0 || ep_gc_parked_count >= others) return; + pthread_mutex_unlock(&ep_gc_mutex); +#ifdef _WIN32 + Sleep(1); +#elif !defined(__wasm__) + usleep(250); +#endif + pthread_mutex_lock(&ep_gc_mutex); + } +} + +/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */ +static void ep_gc_start_the_world(void) { + ep_gc_stop_requested = 0; + pthread_cond_broadcast(&ep_gc_resume_cond); +} + +static void ep_gc_register_thread(void* stack_bottom) { + ep_thread_local_bottom = stack_bottom; + ep_thread_local_top = stack_bottom; + + pthread_mutex_lock(&ep_gc_mutex); + int slot = -1; + for (int i = 0; i < ep_num_threads; i++) { + if (!ep_thread_active[i]) { + slot = i; + break; + } + } + if (slot == -1 && ep_num_threads < EP_MAX_THREADS) { + slot = ep_num_threads++; + } + if (slot != -1) { + ep_thread_tops[slot] = &ep_thread_local_top; + ep_thread_bottoms[slot] = stack_bottom; + /* Allocate or reuse heap state for this slot */ + if (!ep_thread_gc_states[slot]) { + ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState)); + } + ep_thread_gc_states[slot]->sp = 0; + ep_thread_slot = slot; + __sync_synchronize(); /* Memory barrier: state must be visible before active */ + ep_thread_active[slot] = 1; + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +static void ep_gc_unregister_thread(void) { + pthread_mutex_lock(&ep_gc_mutex); + for (int i = 0; i < ep_num_threads; i++) { + if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) { + /* Zero root count FIRST — even if ep_gc_mark races past the + active check, it will see sp=0 and walk no roots instead + of dereferencing stale __thread pointers */ + if (ep_thread_gc_states[i]) { + ep_thread_gc_states[i]->sp = 0; + } + __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */ + ep_thread_active[i] = 0; + ep_thread_slot = -1; + break; + } + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; } + +/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */ +typedef struct { + void* key; + EpGCObject* value; +} EpGCEntry; + +static EpGCEntry* ep_gc_table = NULL; +static long long ep_gc_table_cap = 0; +static long long ep_gc_table_size = 0; + +/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap) + with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4 + bits are always 0 and only every 16th bucket was ever a home slot. That caused + catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's + rehash became O(n^2), which (under the single global GC mutex) wedged the whole + node when a large object list was freed. A splitmix64 finalizer avalanches all + bits, so even the low bits taken by the (cap-1) mask are well distributed. */ +static inline long long ep_gc_index(void* key, long long cap) { + uint64_t z = (uint64_t)(uintptr_t)key; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; + z = z ^ (z >> 31); + return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */ +} + +/* Insert without growing — assumes a free slot exists. Used by the resize and by + ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of + the table mid-iteration. */ +static void ep_gc_table_place(void* key, EpGCObject* value) { + long long idx = ep_gc_index(key, ep_gc_table_cap); + while (ep_gc_table[idx].key != NULL) { + if (ep_gc_table[idx].key == key) { + ep_gc_table[idx].value = value; + return; + } + idx = (idx + 1) & (ep_gc_table_cap - 1); + } + ep_gc_table[idx].key = key; + ep_gc_table[idx].value = value; + ep_gc_table_size++; +} + +static void ep_gc_table_insert(void* key, EpGCObject* value) { + if (ep_gc_table_size * 2 >= ep_gc_table_cap) { + long long old_cap = ep_gc_table_cap; + long long new_cap = old_cap == 0 ? 512 : old_cap * 2; + EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry)); + EpGCEntry* old_table = ep_gc_table; + ep_gc_table = new_table; + ep_gc_table_cap = new_cap; + ep_gc_table_size = 0; + for (long long i = 0; i < old_cap; i++) { + if (old_table[i].key != NULL) { + ep_gc_table_place(old_table[i].key, old_table[i].value); + } + } + free(old_table); + } + ep_gc_table_place(key, value); +} + +static EpGCObject* ep_gc_table_get(void* key) { + if (ep_gc_table_cap == 0) return NULL; + long long idx = ep_gc_index(key, ep_gc_table_cap); + while (ep_gc_table[idx].key != NULL) { + if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value; + idx = (idx + 1) & (ep_gc_table_cap - 1); + } + return NULL; +} + +static void ep_gc_table_remove(void* key) { + if (ep_gc_table_cap == 0) return; + long long idx = ep_gc_index(key, ep_gc_table_cap); + while (ep_gc_table[idx].key != NULL) { + if (ep_gc_table[idx].key == key) { + ep_gc_table[idx].key = NULL; + ep_gc_table[idx].value = NULL; + ep_gc_table_size--; + /* Backward-shift rehash of the rest of this cluster. Re-place (no + resize: size is not growing) so a mid-iteration realloc can never + free the table out from under this loop. */ + long long next_idx = (idx + 1) & (ep_gc_table_cap - 1); + while (ep_gc_table[next_idx].key != NULL) { + void* rehash_key = ep_gc_table[next_idx].key; + EpGCObject* rehash_val = ep_gc_table[next_idx].value; + ep_gc_table[next_idx].key = NULL; + ep_gc_table[next_idx].value = NULL; + ep_gc_table_size--; + ep_gc_table_place(rehash_key, rehash_val); + next_idx = (next_idx + 1) & (ep_gc_table_cap - 1); + } + return; + } + idx = (idx + 1) & (ep_gc_table_cap - 1); + } +} + + + +/* Register a new GC object */ +static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) { + if (!ptr) return NULL; + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */ + EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject)); + if (!obj) { + pthread_mutex_unlock(&ep_gc_mutex); + return NULL; + } + obj->kind = kind; + obj->marked = 0; + obj->ptr = ptr; + obj->size = 0; + obj->num_fields = 0; + obj->generation = 0; + obj->next = ep_gc_head; + ep_gc_head = obj; + ep_gc_count++; + ep_gc_nursery_count++; + ep_gc_table_insert(ptr, obj); + pthread_mutex_unlock(&ep_gc_mutex); + return obj; +} + +/* Find GC object by pointer. + Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table + concurrently (from another thread's allocation). Mutator-side callers + (write barrier, free_struct/free_map/free_list, to-string) must use this + locking variant; code already holding the mutex (mark/sweep) calls + ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */ +static EpGCObject* ep_gc_find(void* ptr) { + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint */ + EpGCObject* obj = ep_gc_table_get(ptr); + pthread_mutex_unlock(&ep_gc_mutex); + return obj; +} + +/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0). + The whole operation runs under ep_gc_mutex so the table lookups and the + remembered-set update see a consistent table (no race with a concurrent + resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */ +static void ep_gc_write_barrier(void* host_ptr, long long val) { + if (val == 0) return; + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */ + EpGCObject* host_obj = ep_gc_table_get(host_ptr); + EpGCObject* val_obj = ep_gc_table_get((void*)val); + if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) { + /* Check if already in remembered set */ + int found = 0; + for (long long i = 0; i < ep_gc_remembered_size; i++) { + if (ep_gc_remembered_set[i] == (void*)val) { + found = 1; + break; + } + } + if (!found) { + if (ep_gc_remembered_size >= ep_gc_remembered_cap) { + long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2; + void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*)); + if (new_set) { + ep_gc_remembered_set = new_set; + ep_gc_remembered_cap = new_cap; + } + } + if (ep_gc_remembered_size < ep_gc_remembered_cap) { + ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val; + } + } + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +/* Forward declarations for list type (needed by GC mark) */ +typedef struct { + long long* data; + long long length; + long long capacity; +} EpList; + +/* A real heap object (list/map/string) is malloc'd, so its address is far above + the never-mapped first page. EP values that are NOT pointers — small ints, + booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096). + Guarding the object accessors with this turns "deref a non-pointer" (the cause + of the read_transcripts segfault, and that whole class) into a safe null return + instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */ +#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL) + +/* Mark a single object and recursively mark its children */ +static void ep_gc_mark_object(void* ptr) { + if (!ptr) return; + /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */ + EpGCObject* obj = ep_gc_table_get(ptr); + if (!obj || obj->marked) return; + obj->marked = 1; + + if (obj->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)ptr; + for (long long i = 0; i < list->length; i++) { + long long val = list->data[i]; + if (val != 0) { + ep_gc_mark_object((void*)val); + } + } + } else if (obj->kind == EP_OBJ_STRUCT) { + long long* fields = (long long*)ptr; + for (long long i = 0; i < obj->num_fields; i++) { + if (fields[i] != 0) { + ep_gc_mark_object((void*)fields[i]); + } + } + } else if (obj->kind == EP_OBJ_MAP) { + if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr); + } +} + +/* Mark a single object and recursively mark its children (only if it is Gen 0) */ +static void ep_gc_mark_object_minor(void* ptr) { + if (!ptr) return; + /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */ + EpGCObject* obj = ep_gc_table_get(ptr); + if (!obj || obj->generation != 0 || obj->marked) return; + obj->marked = 1; + + if (obj->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)ptr; + for (long long i = 0; i < list->length; i++) { + long long val = list->data[i]; + if (val != 0) { + ep_gc_mark_object_minor((void*)val); + } + } + } else if (obj->kind == EP_OBJ_STRUCT) { + long long* fields = (long long*)ptr; + for (long long i = 0; i < obj->num_fields; i++) { + if (fields[i] != 0) { + ep_gc_mark_object_minor((void*)fields[i]); + } + } + } else if (obj->kind == EP_OBJ_MAP) { + if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr); + } +} + +/* Conservatively scan every registered thread's C stack and mark any word that + looks like a tracked pointer. The collector spills its own registers and + publishes its top here; all other threads are parked at a safepoint with their + registers spilled and top published (ep_gc_park_if_stopped), so their stacks + are frozen. This complements the precise shadow stacks: it catches roots held + only in registers/temporaries (e.g. a freshly allocated object not yet stored + into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find. + + Only run on MAJOR collections: minor collections rely on the precise shadow + stacks plus the write barrier's remembered set (the standard generational + approach), so they do no stack scan at all — which means there is no racy + cross-thread stack read on the frequent minor path either. The expensive + full-stack scan is paid only on the rarer major collection, where it pins + any long-lived object reachable only via a register across many GCs. + + Marked no_sanitize_address: a conservative scan deliberately reads whole stack + ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */ +#if defined(__SANITIZE_ADDRESS__) +# define EP_NO_ASAN __attribute__((no_sanitize_address)) +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define EP_NO_ASAN __attribute__((no_sanitize_address)) +# endif +#endif +#ifndef EP_NO_ASAN +# define EP_NO_ASAN +#endif +EP_NO_ASAN +static void ep_gc_scan_thread_stacks(void) { + jmp_buf _regs; + volatile char _top_marker; + memset(&_regs, 0, sizeof(_regs)); + setjmp(_regs); /* spill the collector's own registers onto its stack */ + /* Publish the LOWEST of our own local addresses as this thread's live top, so the + scanned range covers both the stack marker and the register-spill buffer whatever + order the compiler laid them out (a missed _regs would drop a register-only root). */ + { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs; + ep_thread_local_top = (void*)((_a < _b) ? _a : _b); } + for (int t = 0; t < ep_num_threads; t++) { + if (!ep_thread_active[t]) continue; + if (!ep_thread_tops[t]) continue; + void** start = (void**)*ep_thread_tops[t]; + void** end = (void**)ep_thread_bottoms[t]; + if (!start || !end) continue; + if (start > end) { void** tmp = start; start = end; end = tmp; } + for (void** cur = start; cur < end; cur++) { + void* p = *cur; + if (p) ep_gc_mark_object(p); + } + } +} + +/* Mark phase: traverse from ALL threads' explicit GC roots. + Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */ +static void ep_gc_mark(void) { + ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */ + for (int t = 0; t < ep_num_threads; t++) { + if (!ep_thread_active[t]) continue; + EpThreadGCState* state = ep_thread_gc_states[t]; + if (!state) continue; + int sp = state->sp; + if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue; + for (int i = 0; i < sp; i++) { + long long* root_ptr = state->roots[i]; + if (!root_ptr) continue; + long long val = *root_ptr; + if (val != 0) { + ep_gc_mark_object((void*)val); + } + } + } + /* Also mark from main thread's local root stack (thread 0 / unregistered) */ + int local_sp = ep_gc_root_sp; + if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS; + for (int i = 0; i < local_sp; i++) { + long long val = *ep_gc_root_stack[i]; + if (val != 0) { + ep_gc_mark_object((void*)val); + } + } + /* Mark active tasks in the scheduler run queue */ + EpTask* task = ep_run_queue_head; + while (task) { + if (task->fut) { + ep_gc_mark_object((void*)task->fut); + } + if (task->args && task->args_size_bytes > 0) { + long long* ptr = (long long*)task->args; + for (int i = 0; i < task->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object((void*)val); + } + } + task = task->next; + } + /* Mark active tasks in the timers queue */ + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task) { + EpTask* t = timer->task; + if (t->fut) { + ep_gc_mark_object((void*)t->fut); + } + if (t->args && t->args_size_bytes > 0) { + long long* ptr = (long long*)t->args; + for (int i = 0; i < t->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object((void*)val); + } + } + } + timer = timer->next; + } + /* Mark top-level constant/global variables (roots outside any frame) */ + if (ep_gc_mark_globals_major) ep_gc_mark_globals_major(); + /* Scan all registered channel buffers — values in-transit have no root */ + if (ep_gc_scan_channels_major) ep_gc_scan_channels_major(); +} + +/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it + finds. This closes a use-after-free on the frequent minor path: a freshly-allocated + argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating + h() — lives only on the C stack / in registers and is not yet on the precise shadow + stack, so a minor collection triggered mid-expression would otherwise free it. Scanning + ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap + (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by + ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held + roots onto the stack so the scan can see them. */ +EP_NO_ASAN +static void ep_gc_scan_own_stack_minor(void) { + jmp_buf _regs; + volatile char _marker; + memset(&_regs, 0, sizeof(_regs)); + setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */ + void* bottom = ep_thread_local_bottom; + if (!bottom) return; + /* Start at the LOWEST of our own local addresses so the scanned range covers both + the current stack top (_marker) and the register-spill buffer (_regs), regardless + of how the compiler ordered these locals on the stack. Missing _regs would drop a + root held only in a callee-saved register -> a rare use-after-free. */ + char* a = (char*)(void*)&_marker; + char* b = (char*)(void*)&_regs; + char* lo = (a < b) ? a : b; + void** start = (void**)lo; + void** end = (void**)bottom; + if (start > end) { void** tmp = start; start = end; end = tmp; } + for (void** cur = start; cur < end; cur++) { + void* p = *cur; + if (p) ep_gc_mark_object_minor(p); + } +} + +static void ep_gc_mark_minor(void) { + /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument + temporaries (only on the stack / in registers, not yet on the shadow stack) that a + minor collection mid-expression would otherwise free. Own-thread only, so race-free. */ + ep_gc_scan_own_stack_minor(); + for (int t = 0; t < ep_num_threads; t++) { + if (!ep_thread_active[t]) continue; + EpThreadGCState* state = ep_thread_gc_states[t]; + if (!state) continue; + int sp = state->sp; + if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue; + for (int i = 0; i < sp; i++) { + long long* root_ptr = state->roots[i]; + if (!root_ptr) continue; + long long val = *root_ptr; + if (val != 0) { + ep_gc_mark_object_minor((void*)val); + } + } + } + int local_sp = ep_gc_root_sp; + if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS; + for (int i = 0; i < local_sp; i++) { + long long val = *ep_gc_root_stack[i]; + if (val != 0) { + ep_gc_mark_object_minor((void*)val); + } + } + /* Mark active tasks in the scheduler run queue for minor collection */ + EpTask* task = ep_run_queue_head; + while (task) { + if (task->fut) { + ep_gc_mark_object_minor((void*)task->fut); + } + if (task->args && task->args_size_bytes > 0) { + long long* ptr = (long long*)task->args; + for (int i = 0; i < task->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object_minor((void*)val); + } + } + task = task->next; + } + /* Mark active tasks in the timers queue for minor collection */ + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task) { + EpTask* t = timer->task; + if (t->fut) { + ep_gc_mark_object_minor((void*)t->fut); + } + if (t->args && t->args_size_bytes > 0) { + long long* ptr = (long long*)t->args; + for (int i = 0; i < t->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object_minor((void*)val); + } + } + } + timer = timer->next; + } + /* Also mark from the remembered set */ + for (long long i = 0; i < ep_gc_remembered_size; i++) { + ep_gc_mark_object_minor(ep_gc_remembered_set[i]); + } + /* Mark top-level constant/global variables (roots outside any frame) */ + if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor(); + /* Scan all registered channel buffers — values in-transit have no root */ + if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor(); +} + +static void ep_gc_sweep_minor(void) { + EpGCObject** cur = &ep_gc_head; + while (*cur) { + if ((*cur)->generation == 0) { + if (!(*cur)->marked) { + EpGCObject* garbage = *cur; + *cur = garbage->next; + ep_gc_table_remove(garbage->ptr); + if (garbage->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)garbage->ptr; + if (list) { + free(list->data); + free(list); + } + } else if (garbage->kind == EP_OBJ_STRING) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_STRUCT) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_CLOSURE) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_MAP) { + /* EpMap layout: entries*, capacity, size. Free entries then map. */ + void** map_fields = (void**)garbage->ptr; + if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */ + free(garbage->ptr); + } + free(garbage); + ep_gc_count--; + ep_gc_nursery_count--; + } else { + (*cur)->marked = 0; + (*cur)->generation = 1; + ep_gc_nursery_count--; + cur = &(*cur)->next; + } + } else { + cur = &(*cur)->next; + } + } + ep_gc_remembered_size = 0; +} + +static void ep_gc_sweep_major(void) { + EpGCObject** cur = &ep_gc_head; + while (*cur) { + if (!(*cur)->marked) { + EpGCObject* garbage = *cur; + *cur = garbage->next; + ep_gc_table_remove(garbage->ptr); + if (garbage->generation == 0) { + ep_gc_nursery_count--; + } + if (garbage->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)garbage->ptr; + if (list) { + free(list->data); + free(list); + } + } else if (garbage->kind == EP_OBJ_STRING) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_STRUCT) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_CLOSURE) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_MAP) { + void** map_fields = (void**)garbage->ptr; + if (map_fields && map_fields[0]) free(map_fields[0]); + free(garbage->ptr); + } + free(garbage); + ep_gc_count--; + } else { + (*cur)->marked = 0; + if ((*cur)->generation == 0) { + (*cur)->generation = 1; + ep_gc_nursery_count--; + } + cur = &(*cur)->next; + } + } + ep_gc_remembered_size = 0; +} + +static void ep_gc_collect_minor(void) { + if (!ep_gc_enabled) return; + ep_gc_minor_count++; + ep_gc_mark_minor(); + ep_gc_sweep_minor(); +} + +static void ep_gc_collect_major(void) { + if (!ep_gc_enabled) return; + ep_gc_major_count++; + ep_gc_mark(); + ep_gc_sweep_major(); + ep_gc_threshold = ep_gc_count * 2; + if (ep_gc_threshold < 4096) ep_gc_threshold = 4096; +} + +/* Run a full GC collection — caller MUST hold ep_gc_mutex */ +static void ep_gc_collect(void) { + ep_gc_collect_major(); +} + +/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function + GC safepoint: if another thread has stopped the world, park here until it's done. */ +static void ep_gc_maybe_collect(void) { + if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */ + /* Safepoint: lock-free fast check, then park under the lock if a collection + is in progress on another thread. Keeps the no-GC path lock-free. */ + if (ep_gc_stop_requested) { + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); + pthread_mutex_unlock(&ep_gc_mutex); + } + /* Fast path: check thresholds before acquiring mutex. + Counters are only incremented under the mutex, so worst case + we miss one collection cycle — safe trade-off for avoiding + a mutex lock/unlock (~20-50ns) on every function call. */ + if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return; + EP_GC_UPDATE_TOP(); + pthread_mutex_lock(&ep_gc_mutex); + /* Another thread may have started collecting between the check and the lock — + park instead of racing it, then re-check thresholds under the lock. */ + ep_gc_park_if_stopped(); + if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) { + ep_gc_stop_the_world(); + if (ep_gc_nursery_count >= ep_gc_nursery_threshold) { + ep_gc_collect_minor(); + } + if (ep_gc_count >= ep_gc_threshold) { + ep_gc_collect_major(); + } + ep_gc_start_the_world(); + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +/* Unregister an object (for explicit free — removes from GC tracking) */ +static void ep_gc_unregister(void* ptr) { + if (!ptr) return; + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */ + /* Clean up references from the remembered set to prevent dangling pointers */ + for (long long i = 0; i < ep_gc_remembered_size; ) { + if (ep_gc_remembered_set[i] == ptr) { + for (long long j = i; j < ep_gc_remembered_size - 1; j++) { + ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1]; + } + ep_gc_remembered_size--; + } else { + i++; + } + } + ep_gc_table_remove(ptr); + EpGCObject** cur = &ep_gc_head; + while (*cur) { + if ((*cur)->ptr == ptr) { + EpGCObject* found = *cur; + *cur = found->next; + if (found->generation == 0) { + ep_gc_nursery_count--; + } + free(found); + ep_gc_count--; + pthread_mutex_unlock(&ep_gc_mutex); + return; + } + cur = &(*cur)->next; + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +/* Cleanup all remaining GC objects (called at program exit) */ +static void ep_gc_shutdown(void) { + ep_gc_enabled = 0; + /* Only free GC bookkeeping structures, not the tracked objects themselves. + The RAII auto-cleanup has already freed owned objects, and the OS will + reclaim everything else on process exit. Attempting to free individual + objects here causes double-free aborts when RAII and GC both track + the same allocation. */ + EpGCObject* cur = ep_gc_head; + while (cur) { + EpGCObject* next = cur->next; + free(cur); /* free the GCObject wrapper only */ + cur = next; + } + ep_gc_head = NULL; + ep_gc_count = 0; + if (ep_gc_table) { + free(ep_gc_table); + ep_gc_table = NULL; + } + ep_gc_table_cap = 0; + ep_gc_table_size = 0; +} + +/* ========== End Garbage Collector ========== */ + +long long create_list(void); +long long append_list(long long list_ptr, long long value); +long long get_list(long long list_ptr, long long index); +long long set_list(long long list_ptr, long long index, long long value); +long long length_list(long long list_ptr); +long long free_list(long long list_ptr); +long long pop_list(long long list_ptr); +long long remove_list(long long list_ptr, long long index); +char* string_from_list(long long list_ptr); +long long string_to_list(const char* s); +long long string_length(const char* s); +long long display_string(const char* s); +long long file_read(long long path_val); +long long file_write(long long path_val, long long content_val); +long long file_append(long long path_val, long long content_val); +long long file_exists(long long path_val); +long long string_contains(long long s_val, long long sub_val); +long long string_index_of(long long s_val, long long sub_val); +long long string_replace(long long s_val, long long old_val, long long new_val); +long long string_upper(long long s_val); +long long string_lower(long long s_val); +long long string_trim(long long s_val); +long long string_split(long long s_val, long long delim_val); +long long char_at(long long s_val, long long index); +long long char_from_code(long long code); +long long ep_abs(long long n); +long long json_get_string(long long json_val, long long key_val); +long long json_get_int(long long json_val, long long key_val); +long long json_get_bool(long long json_val, long long key_val); +long long ep_sha1(long long data_val); +long long ep_net_recv_bytes(long long fd, long long count); +long long channel_try_recv(long long chan_ptr, long long out_ptr); +long long channel_has_data(long long chan_ptr); +long long channel_select(long long channels_list, long long timeout_ms); +long long ep_auto_to_string(long long val); + +typedef struct EpChannel_ { + long long* data; + long long capacity; + long long head; + long long tail; + long long size; + ep_mutex_t mutex; + ep_cond_t cond_recv; + ep_cond_t cond_send; +} EpChannel; + +/* Global channel registry — allows GC to scan values in-transit in channel buffers. + Without this, an object sent to a channel but not yet received has NO GC root: + the sender has popped it, the receiver hasn't pushed it, and the channel buffer + is not scanned. The GC sweeps it → receiver gets a dangling pointer. */ +#define EP_MAX_CHANNELS 1024 +static EpChannel* ep_channel_registry[EP_MAX_CHANNELS]; +static int ep_channel_count = 0; +static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void ep_register_channel(EpChannel* chan) { + pthread_mutex_lock(&ep_channel_registry_mutex); + if (ep_channel_count < EP_MAX_CHANNELS) { + ep_channel_registry[ep_channel_count++] = chan; + } + pthread_mutex_unlock(&ep_channel_registry_mutex); +} + +/* Channel scanning implementations — called by GC mark via function pointers. + These are defined here (after EpChannel) so they can access struct fields. */ +static void ep_gc_mark_object(void* ptr); /* forward decl */ +static void ep_gc_mark_object_minor(void* ptr); /* forward decl */ + +static void ep_gc_scan_channels_major_impl(void) { + pthread_mutex_lock(&ep_channel_registry_mutex); + for (int c = 0; c < ep_channel_count; c++) { + EpChannel* chan = ep_channel_registry[c]; + if (!chan || chan->size <= 0) continue; + ep_mutex_lock(&chan->mutex); + for (long long j = 0; j < chan->size; j++) { + long long idx = (chan->head + j) % chan->capacity; + long long val = chan->data[idx]; + if (val != 0) ep_gc_mark_object((void*)val); + } + ep_mutex_unlock(&chan->mutex); + } + pthread_mutex_unlock(&ep_channel_registry_mutex); +} + +static void ep_gc_scan_channels_minor_impl(void) { + pthread_mutex_lock(&ep_channel_registry_mutex); + for (int c = 0; c < ep_channel_count; c++) { + EpChannel* chan = ep_channel_registry[c]; + if (!chan || chan->size <= 0) continue; + ep_mutex_lock(&chan->mutex); + for (long long j = 0; j < chan->size; j++) { + long long idx = (chan->head + j) % chan->capacity; + long long val = chan->data[idx]; + if (val != 0) ep_gc_mark_object_minor((void*)val); + } + ep_mutex_unlock(&chan->mutex); + } + pthread_mutex_unlock(&ep_channel_registry_mutex); +} + +long long create_channel(void) { + EpChannel* chan = malloc(sizeof(EpChannel)); + if (!chan) return 0; + chan->capacity = 1024; + chan->data = malloc(chan->capacity * sizeof(long long)); + chan->head = 0; + chan->tail = 0; + chan->size = 0; + ep_mutex_init(&chan->mutex); + ep_cond_init(&chan->cond_recv); + ep_cond_init(&chan->cond_send); + ep_register_channel(chan); + return (long long)chan; +} + +long long send_channel(long long chan_ptr, long long value) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + /* Suppress GC during channel operations. The blocking condvar wait + can interleave with GC mark/sweep on another thread, causing + use-after-free when the GC sweeps objects that are live on a + thread currently blocked in send/receive. Channel buffers contain + raw long long values (not GC-tracked pointers), so suppressing + GC here is safe. */ + int gc_was_enabled = ep_gc_enabled; + ep_gc_enabled = 0; + ep_mutex_lock(&chan->mutex); + while (chan->size >= chan->capacity) { + ep_cond_wait(&chan->cond_send, &chan->mutex); + } + chan->data[chan->tail] = value; + chan->tail = (chan->tail + 1) % chan->capacity; + chan->size += 1; + ep_cond_signal(&chan->cond_recv); + ep_mutex_unlock(&chan->mutex); + ep_gc_enabled = gc_was_enabled; + return value; +} + +long long receive_channel(long long chan_ptr) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + /* Suppress GC during channel receive — same rationale as send_channel */ + int gc_was_enabled = ep_gc_enabled; + ep_gc_enabled = 0; + ep_mutex_lock(&chan->mutex); + while (chan->size <= 0) { + ep_cond_wait(&chan->cond_recv, &chan->mutex); + } + long long value = chan->data[chan->head]; + chan->head = (chan->head + 1) % chan->capacity; + chan->size -= 1; + ep_cond_signal(&chan->cond_send); + ep_mutex_unlock(&chan->mutex); + ep_gc_enabled = gc_was_enabled; + return value; +} + +// Non-blocking receive — returns 1 if data was available, 0 if channel empty +long long channel_try_recv(long long chan_ptr, long long out_ptr) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + ep_mutex_lock(&chan->mutex); + if (chan->size <= 0) { + ep_mutex_unlock(&chan->mutex); + return 0; + } + long long value = chan->data[chan->head]; + chan->head = (chan->head + 1) % chan->capacity; + chan->size -= 1; + ep_cond_signal(&chan->cond_send); + ep_mutex_unlock(&chan->mutex); + if (out_ptr) { + *((long long*)out_ptr) = value; + } + return 1; +} + +// Check if channel has data without consuming it +long long channel_has_data(long long chan_ptr) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + ep_mutex_lock(&chan->mutex); + int has = (chan->size > 0) ? 1 : 0; + ep_mutex_unlock(&chan->mutex); + return has; +} + +// Select: wait for any of N channels to have data, with timeout in ms +// channels_list is a list of channel pointers +// Returns index (0-based) of first ready channel, or -1 on timeout +long long channel_select(long long channels_list, long long timeout_ms) { + EpList* list = (EpList*)channels_list; + if (!list || list->length == 0) return -1; + +#ifdef _WIN32 + ULONGLONG start_tick = GetTickCount64(); +#else + struct timespec start, now; + clock_gettime(CLOCK_MONOTONIC, &start); +#endif + + while (1) { + // Poll all channels + for (long long i = 0; i < list->length; i++) { + EpChannel* chan = (EpChannel*)list->data[i]; + if (chan) { + ep_mutex_lock(&chan->mutex); + if (chan->size > 0) { + ep_mutex_unlock(&chan->mutex); + return i; + } + ep_mutex_unlock(&chan->mutex); + } + } + + // Check timeout + if (timeout_ms >= 0) { +#ifdef _WIN32 + ULONGLONG now_tick = GetTickCount64(); + long long elapsed = (long long)(now_tick - start_tick); +#else + clock_gettime(CLOCK_MONOTONIC, &now); + long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000; +#endif + if (elapsed >= timeout_ms) return -1; + } + + // Brief sleep to avoid busy-wait +#ifdef _WIN32 + Sleep(1); +#else + usleep(1000); // 1ms +#endif + } +} + +#ifdef __wasm__ +long long ep_net_connect(const char* host, long long port) { + (void)host; (void)port; + return -1; +} + +long long ep_net_listen(long long port) { + (void)port; + return -1; +} + +long long ep_net_accept(long long server_fd) { + (void)server_fd; + return -1; +} + +long long ep_net_send(long long fd, const char* data) { + (void)fd; (void)data; + return 0; +} + +char* ep_net_recv(long long fd, long long max_len) { + (void)fd; (void)max_len; + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + return empty; +} + +long long ep_net_close(long long fd) { + (void)fd; + return -1; +} + +long long ep_sleep_ms(long long ms) { + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&ts, NULL); + return 0; +} + +long long ep_system(long long cmd) { + (void)cmd; + return -1; +} + +long long ep_play_sound(long long path) { + (void)path; + return -1; +} + +long long ep_dlopen(long long path) { + (void)path; + return 0; +} + +long long ep_dlsym(long long handle, long long name) { + (void)handle; (void)name; + return 0; +} + +long long ep_dlclose(long long handle) { + (void)handle; + return 0; +} +#else +long long ep_net_connect(const char* host, long long port) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) return -1; + struct hostent* server = gethostbyname(host); + if (!server) { +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + return -1; + } + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length); + serv_addr.sin_port = htons(port); +#ifdef _WIN32 + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + closesocket(sockfd); + return -1; + } +#else + // Bounded connect: an unreachable peer must not block ~75s on the OS SYN + // timeout (this stalled node startup). Non-blocking connect + 5s select, then + // restore blocking mode for the rest of the session. + int _ep_flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK); + int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + if (_ep_cr < 0) { + if (errno != EINPROGRESS) { close(sockfd); return -1; } + fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset); + struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0; + int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv); + if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error + int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) { + close(sockfd); + return -1; + } + } + fcntl(sockfd, F_SETFL, _ep_flags); +#endif + return sockfd; +} + +long long ep_net_listen(long long port) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) return -1; + int opt = 1; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + return -1; + } + if (listen(sockfd, 10) < 0) { +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + return -1; + } + return sockfd; +} + +long long ep_net_accept(long long server_fd) { + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen); + if (newsockfd >= 0) { + /* Bound how long a single recv/send may block so a slow or silent + client cannot pin a handler thread forever (slowloris). */ + struct timeval tv; + tv.tv_sec = 30; + tv.tv_usec = 0; + setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); + setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); + } + return newsockfd; +} + +long long ep_net_send(long long fd, const char* data) { + if (!data) return 0; + /* send() may write fewer bytes than requested (partial write under load/ + backpressure). A single send() therefore silently truncated large IPC + responses, cutting agent replies mid-stream. Loop until all bytes are sent. */ + size_t total = strlen(data); + size_t off = 0; + while (off < total) { + ssize_t n = send((int)fd, data + off, total - off, 0); + if (n <= 0) break; + off += (size_t)n; + } + return (long long)off; +} + +char* ep_net_recv(long long fd, long long max_len) { + char* buf = malloc(max_len + 1); + if (!buf) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + return empty; + } +#ifdef _WIN32 + int n = recv((int)fd, buf, (int)max_len, 0); +#else + ssize_t n = recv((int)fd, buf, max_len, 0); +#endif + if (n < 0) n = 0; + buf[n] = '\0'; + return buf; +} + +long long ep_net_close(long long fd) { +#ifdef _WIN32 + return closesocket((int)fd); +#else + return close((int)fd); +#endif +} + +long long ep_sleep_ms(long long ms) { +#ifdef _WIN32 + Sleep((DWORD)ms); +#else + usleep((useconds_t)(ms * 1000)); +#endif + return 0; +} + +long long ep_system(long long cmd) { + return (long long)system((const char*)cmd); +} + +long long ep_play_sound(long long path) { + char cmd[512]; + snprintf(cmd, sizeof(cmd), "afplay '%s' &", (const char*)path); + return (long long)system(cmd); +} + +/* ========== Dynamic Library Loading (FFI) ========== */ +#ifndef _WIN32 +#include +#endif + +long long ep_dlopen(long long path) { +#ifdef _WIN32 + HMODULE h = LoadLibraryA((const char*)path); + return (long long)h; +#else + const char* p = (const char*)path; + void* handle = dlopen(p, RTLD_LAZY); + return (long long)handle; +#endif +} + +long long ep_dlsym(long long handle, long long name) { +#ifdef _WIN32 + FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name); + return (long long)sym; +#else + void* sym = dlsym((void*)handle, (const char*)name); + return (long long)sym; +#endif +} + +long long ep_dlclose(long long handle) { +#ifdef _WIN32 + return (long long)FreeLibrary((HMODULE)handle); +#else + return (long long)dlclose((void*)handle); +#endif +} +#endif + +/* Call a function pointer with 0..6 arguments. + These are type-punned through long long — the C calling convention + makes this work for integer and pointer arguments. */ +typedef long long (*ep_fn0)(void); +typedef long long (*ep_fn1)(long long); +typedef long long (*ep_fn2)(long long, long long); +typedef long long (*ep_fn3)(long long, long long, long long); +typedef long long (*ep_fn4)(long long, long long, long long, long long); +typedef long long (*ep_fn5)(long long, long long, long long, long long, long long); +typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long); + +long long ep_dlcall0(long long fptr) { + return ((ep_fn0)fptr)(); +} +long long ep_dlcall1(long long fptr, long long a0) { + return ((ep_fn1)fptr)(a0); +} +long long ep_dlcall2(long long fptr, long long a0, long long a1) { + return ((ep_fn2)fptr)(a0, a1); +} +long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) { + return ((ep_fn3)fptr)(a0, a1, a2); +} +long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) { + return ((ep_fn4)fptr)(a0, a1, a2, a3); +} +long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) { + return ((ep_fn5)fptr)(a0, a1, a2, a3, a4); +} +long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) { + return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5); +} +long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) { + return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6); +} +long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) { + return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7); +} +long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) { + return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8); +} +long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) { + return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); +} + +/* ========== Float FFI: ep_dlcall_f* ========== */ +/* For calling C functions that accept/return double values. + Arguments are passed as long long (bit-punned doubles). + Return value is a double bit-punned back to long long. + Use ep_double_to_bits() / ep_bits_to_double() to convert. */ + +typedef union { long long i; double f; } ep_float_bits; + +static inline double ep_ll_to_double(long long v) { + ep_float_bits u; u.i = v; return u.f; +} +static inline long long ep_double_to_ll(double v) { + ep_float_bits u; u.f = v; return u.i; +} + +/* Convert between ErnosPlain float representation and raw bits */ +long long ep_double_to_bits(long long float_val) { + /* float_val is already an EP Float stored as long long bits */ + return float_val; +} +long long ep_bits_to_double(long long bits) { + return bits; +} + +/* Float function pointer typedefs */ +typedef double (*ep_ff0)(void); +typedef double (*ep_ff1)(double); +typedef double (*ep_ff2)(double, double); +typedef double (*ep_ff3)(double, double, double); +typedef double (*ep_ff4)(double, double, double, double); +typedef double (*ep_ff5)(double, double, double, double, double); +typedef double (*ep_ff6)(double, double, double, double, double, double); + +/* Call functions that take doubles and return double */ +long long ep_dlcall_f0(long long fptr) { + return ep_double_to_ll(((ep_ff0)fptr)()); +} +long long ep_dlcall_f1(long long fptr, long long a0) { + return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0))); +} +long long ep_dlcall_f2(long long fptr, long long a0, long long a1) { + return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1))); +} +long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) { + return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2))); +} +long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) { + return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3))); +} +long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) { + return ep_double_to_ll(((ep_ff5)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4))); +} +long long ep_dlcall_f6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) { + return ep_double_to_ll(((ep_ff6)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4), ep_ll_to_double(a5))); +} + +/* Variants that take doubles but return int (for comparison functions etc.) */ +typedef long long (*ep_fdi1)(double); +typedef long long (*ep_fdi2)(double, double); +typedef long long (*ep_fdi3)(double, double, double); + +long long ep_dlcall_fd1(long long fptr, long long a0) { + return ((ep_fdi1)fptr)(ep_ll_to_double(a0)); +} +long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) { + return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)); +} +long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) { + return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)); +} +/* ========== End Float FFI ========== */ +/* ========== End Dynamic Library Loading ========== */ + +unsigned long hash_string(const char* str) { + unsigned long hash = 5381; + int c; + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; + } + return hash; +} + +typedef struct { + char* key; + long long value; + int used; +} EpMapEntry; + +typedef struct { + EpMapEntry* entries; + long long capacity; + long long size; +} EpMap; + +/* Map value traversal for GC — walks all entries and marks values. + Called by ep_gc_mark_object() via function pointer. */ +static void ep_gc_mark_map_values_impl(void* ptr) { + EpMap* map = (EpMap*)ptr; + if (!map || !map->entries) return; + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].value != 0) { + ep_gc_mark_object((void*)map->entries[i].value); + } + /* Also mark keys if they are heap strings */ + if (map->entries[i].used && map->entries[i].key != NULL) { + ep_gc_mark_object((void*)map->entries[i].key); + } + } +} + +static void ep_gc_mark_map_values_minor_impl(void* ptr) { + EpMap* map = (EpMap*)ptr; + if (!map || !map->entries) return; + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].value != 0) { + ep_gc_mark_object_minor((void*)map->entries[i].value); + } + if (map->entries[i].used && map->entries[i].key != NULL) { + ep_gc_mark_object_minor((void*)map->entries[i].key); + } + } +} + +long long create_map(void) { + EpMap* map = malloc(sizeof(EpMap)); + if (!map) return 0; + map->capacity = 16; + map->size = 0; + map->entries = calloc(map->capacity, sizeof(EpMapEntry)); + if (!map->entries) { + free(map); + return 0; + } + ep_gc_register(map, EP_OBJ_MAP); + return (long long)map; +} + +static void map_resize(EpMap* map, long long new_capacity) { + EpMapEntry* old_entries = map->entries; + long long old_capacity = map->capacity; + map->capacity = new_capacity; + map->entries = calloc(new_capacity, sizeof(EpMapEntry)); + map->size = 0; + for (long long i = 0; i < old_capacity; i++) { + if (old_entries[i].used && old_entries[i].key != NULL) { + char* key = old_entries[i].key; + long long value = old_entries[i].value; + unsigned long h = hash_string(key) % new_capacity; + while (map->entries[h].used) { + h = (h + 1) % new_capacity; + } + map->entries[h].key = key; + map->entries[h].value = value; + map->entries[h].used = 1; + map->size++; + } + } + free(old_entries); +} + +/* Convert a key value to a string — handles both string pointers and integers */ +static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) { + if (key_val == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; } + /* Check if value is in plausible pointer range for a string */ + if (key_val > 0x100000) { + const char* p = (const char*)(void*)key_val; + unsigned char first = (unsigned char)*p; + if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) { + return p; /* valid string pointer */ + } + } + snprintf(buf, bufsize, "%lld", key_val); + return buf; +} + +long long map_insert(long long map_ptr, long long key_val, long long value) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + if (map->size * 2 >= map->capacity) { + map_resize(map, map->capacity * 2); + } + unsigned long h = hash_string(key) % map->capacity; + while (map->entries[h].used) { + if (strcmp(map->entries[h].key, key) == 0) { + map->entries[h].value = value; + ep_gc_write_barrier((void*)map_ptr, value); + return value; + } + h = (h + 1) % map->capacity; + } + map->entries[h].key = strdup(key); + map->entries[h].value = value; + map->entries[h].used = 1; + map->size++; + ep_gc_write_barrier((void*)map_ptr, value); + return value; +} + +long long map_get_val(long long map_ptr, long long key_val) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + unsigned long h = hash_string(key) % map->capacity; + long long start_h = h; + while (map->entries[h].used) { + if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { + return map->entries[h].value; + } + h = (h + 1) % map->capacity; + if (h == start_h) break; + } + return 0; +} + +/* map_set_str: store a string value (strdup'd copy) under a string key */ +long long map_set_str(long long map_ptr, long long key_val, long long str_val) { + /* Store the string pointer as a long long value — same as map_insert */ + return map_insert(map_ptr, key_val, str_val); +} + +/* map_get_str: retrieve a string value from a map (returns char* as long long) */ +long long map_get_str(long long map_ptr, long long key_val) { + /* Same as map_get_val — the stored long long IS a char* pointer */ + return map_get_val(map_ptr, key_val); +} + +long long map_contains(long long map_ptr, long long key_val) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + unsigned long h = hash_string(key) % map->capacity; + long long start_h = h; + while (map->entries[h].used) { + if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { + return 1; + } + h = (h + 1) % map->capacity; + if (h == start_h) break; + } + return 0; +} + +long long map_delete(long long map_ptr, long long key_val) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + unsigned long h = hash_string(key) % map->capacity; + long long start_h = h; + while (map->entries[h].used) { + if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { + free(map->entries[h].key); + map->entries[h].key = NULL; + map->entries[h].value = 0; + map->entries[h].used = 0; + map->size--; + long long next_h = (h + 1) % map->capacity; + while (map->entries[next_h].used) { + char* k = map->entries[next_h].key; + long long v = map->entries[next_h].value; + map->entries[next_h].key = NULL; + map->entries[next_h].value = 0; + map->entries[next_h].used = 0; + map->size--; + map_insert(map_ptr, (long long)k, v); + free(k); + next_h = (next_h + 1) % map->capacity; + } + return 1; + } + h = (h + 1) % map->capacity; + if (h == start_h) break; + } + return 0; +} + +long long map_keys(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return (long long)create_list(); + long long list = create_list(); + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].key) { + append_list(list, (long long)strdup(map->entries[i].key)); + } + } + return list; +} + +long long map_values(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return (long long)create_list(); + long long list = create_list(); + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].key) { + append_list(list, map->entries[i].value); + } + } + return list; +} + +long long map_size(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return 0; + return map->size; +} + +long long free_map(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return 0; + /* Skip if already freed (idempotent) */ + if (!ep_gc_find(map)) return 0; + ep_gc_unregister(map); + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].key != NULL) { + free(map->entries[i].key); + } + } + free(map->entries); + free(map); + return 0; +} + +typedef struct { + long long* data; + long long capacity; + long long head; + long long tail; + long long size; +} EpDeque; + +long long create_deque(void) { + EpDeque* dq = malloc(sizeof(EpDeque)); + if (!dq) return 0; + dq->capacity = 16; + dq->size = 0; + dq->head = 0; + dq->tail = 0; + dq->data = malloc(dq->capacity * sizeof(long long)); + if (!dq->data) { + free(dq); + return 0; + } + return (long long)dq; +} + +static void deque_resize(EpDeque* dq, long long new_capacity) { + long long* new_data = malloc(new_capacity * sizeof(long long)); + for (long long i = 0; i < dq->size; i++) { + new_data[i] = dq->data[(dq->head + i) % dq->capacity]; + } + free(dq->data); + dq->data = new_data; + dq->capacity = new_capacity; + dq->head = 0; + dq->tail = dq->size; +} + +long long deque_push_back(long long dq_ptr, long long value) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + if (dq->size >= dq->capacity) { + deque_resize(dq, dq->capacity * 2); + } + dq->data[dq->tail] = value; + dq->tail = (dq->tail + 1) % dq->capacity; + dq->size++; + return value; +} + +long long deque_push_front(long long dq_ptr, long long value) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + if (dq->size >= dq->capacity) { + deque_resize(dq, dq->capacity * 2); + } + dq->head = (dq->head - 1 + dq->capacity) % dq->capacity; + dq->data[dq->head] = value; + dq->size++; + return value; +} + +long long deque_pop_back(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq || dq->size == 0) return 0; + dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity; + long long value = dq->data[dq->tail]; + dq->size--; + return value; +} + +long long deque_pop_front(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq || dq->size == 0) return 0; + long long value = dq->data[dq->head]; + dq->head = (dq->head + 1) % dq->capacity; + dq->size--; + return value; +} + +long long deque_length(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + return dq->size; +} + +long long free_deque(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + free(dq->data); + free(dq); + return 0; +} + +/* Filesystem Operations */ +#include +#include + +long long fs_scan_dir(long long path_val) { + const char* path = (const char*)path_val; + long long list_ptr = create_list(); + if (!path) return list_ptr; + DIR* d = opendir(path); + if (!d) return list_ptr; + struct dirent* dir; + while ((dir = readdir(d)) != NULL) { + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { + continue; + } + char* name = strdup(dir->d_name); + append_list(list_ptr, (long long)name); + } + closedir(d); + return list_ptr; +} + +long long fs_copy_file(long long src_val, long long dest_val) { + const char* src = (const char*)src_val; + const char* dest = (const char*)dest_val; + if (!src || !dest) return 0; + FILE* f_src = fopen(src, "rb"); + if (!f_src) return 0; + FILE* f_dest = fopen(dest, "wb"); + if (!f_dest) { + fclose(f_src); + return 0; + } + char buf[4096]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) { + fwrite(buf, 1, n, f_dest); + } + fclose(f_src); + fclose(f_dest); + return 1; +} + +long long fs_delete_file(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + return remove(path) == 0 ? 1 : 0; +} + +long long fs_move_file(long long src_val, long long dest_val) { + const char* src = (const char*)src_val; + const char* dest = (const char*)dest_val; + if (!src || !dest) return 0; + return rename(src, dest) == 0 ? 1 : 0; +} + +long long fs_exists(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + return stat(path, &st) == 0 ? 1 : 0; +} + +long long fs_is_dir(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + if (stat(path, &st) != 0) return 0; + return S_ISDIR(st.st_mode) ? 1 : 0; +} + +long long fs_is_file(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + if (stat(path, &st) != 0) return 0; + return S_ISREG(st.st_mode) ? 1 : 0; +} + +long long fs_get_size(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + if (stat(path, &st) != 0) return 0; + return (long long)st.st_size; +} + +/* HTTP Client */ +#ifdef __wasm__ +long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) { + (void)method_val; (void)url_val; (void)headers_val; (void)body_val; + return (long long)strdup("Error: HTTP request is not supported on WebAssembly"); +} +#else +long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) { + const char* method = (const char*)method_val; + const char* url = (const char*)url_val; + const char* headers = (const char*)headers_val; + const char* body = (const char*)body_val; + if (!method || !url) return (long long)strdup(""); + if (strncmp(url, "http://", 7) != 0) { + return (long long)strdup("Error: only http:// protocol supported"); + } + const char* host_start = url + 7; + const char* path_start = strchr(host_start, '/'); + char host[256]; + char path[1024]; + if (path_start) { + size_t host_len = path_start - host_start; + if (host_len >= sizeof(host)) host_len = sizeof(host) - 1; + strncpy(host, host_start, host_len); + host[host_len] = '\0'; + strncpy(path, path_start, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + } else { + strncpy(host, host_start, sizeof(host) - 1); + host[sizeof(host) - 1] = '\0'; + strcpy(path, "/"); + } + int port = 80; + char* colon = strchr(host, ':'); + if (colon) { + *colon = '\0'; + port = atoi(colon + 1); + } + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) return (long long)strdup("Error: socket creation failed"); + struct hostent* server = gethostbyname(host); + if (!server) { + close(sockfd); + return (long long)strdup("Error: host resolution failed"); + } + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length); + serv_addr.sin_port = htons(port); + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + close(sockfd); + return (long long)strdup("Error: connection failed"); + } + char req[4096]; + size_t body_len = body ? strlen(body) : 0; + int req_len = snprintf(req, sizeof(req), + "%s %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Content-Length: %zu\r\n" + "Connection: close\r\n" + "%s%s" + "\r\n", + method, path, host, body_len, headers ? headers : "", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\n') ? "\r\n" : ""); + if (send(sockfd, req, req_len, 0) < 0) { + close(sockfd); + return (long long)strdup("Error: send failed"); + } + if (body_len > 0) { + if (send(sockfd, body, body_len, 0) < 0) { + close(sockfd); + return (long long)strdup("Error: send body failed"); + } + } + size_t resp_cap = 4096; + size_t resp_len = 0; + char* resp = malloc(resp_cap); + if (!resp) { + close(sockfd); + return (long long)strdup(""); + } + char recv_buf[4096]; + ssize_t n; + while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) { + if (resp_len + n >= resp_cap) { + resp_cap *= 2; + char* new_resp = realloc(resp, resp_cap); + if (!new_resp) { + free(resp); + close(sockfd); + return (long long)strdup("Error: memory allocation failed"); + } + resp = new_resp; + } + memcpy(resp + resp_len, recv_buf, n); + resp_len += n; + } + resp[resp_len] = '\0'; + close(sockfd); + // Strip HTTP headers — return only the body after \r\n\r\n + char* http_body = strstr(resp, "\r\n\r\n"); + if (http_body) { + http_body += 4; + char* result = strdup(http_body); + free(resp); + return (long long)result; + } + return (long long)resp; +} +#endif + +#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits)))) +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +typedef struct { + unsigned char data[64]; + unsigned int datalen; + unsigned long long bitlen; + unsigned int state[8]; +} EP_SHA256_CTX; + +static const unsigned int sha256_k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) { + unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; + e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; + } + ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; + ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; +} + +void ep_sha256_init(EP_SHA256_CTX *ctx) { + ctx->datalen = 0; ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; +} + +void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) { + for (size_t i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + ep_sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) { + unsigned int i = ctx->datalen; + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) ctx->data[i++] = 0x00; + } else { + ctx->data[i++] = 0x80; + while (i < 64) ctx->data[i++] = 0x00; + ep_sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; + ep_sha256_transform(ctx, ctx->data); + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} + +char* ep_sha256(const char* s) { + if (!s) s = ""; + EP_SHA256_CTX ctx; + ep_sha256_init(&ctx); + ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s)); + unsigned char hash[32]; + ep_sha256_final(&ctx, hash); + char* result = malloc(65); + if (result) { + for (int i = 0; i < 32; i++) { + snprintf(result + (i * 2), 3, "%02x", hash[i]); + } + result[64] = '\0'; + } + return result; +} + +/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary + safe), so keys/messages containing NUL bytes hash correctly. Returns a + malloc'd 64-char lowercase hex string. */ +long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) { + const unsigned char* key = (const unsigned char*)key_ptr; + const unsigned char* msg = (const unsigned char*)msg_ptr; + size_t klen = (size_t)key_len; + size_t mlen = (size_t)msg_len; + + unsigned char k0[64]; + memset(k0, 0, sizeof(k0)); + if (klen > 64) { + /* Keys longer than the block size are replaced by their hash. */ + EP_SHA256_CTX kc; + ep_sha256_init(&kc); + ep_sha256_update(&kc, key ? key : (const unsigned char*)"", klen); + unsigned char kh[32]; + ep_sha256_final(&kc, kh); + memcpy(k0, kh, 32); + } else if (key) { + memcpy(k0, key, klen); + } + + unsigned char ipad[64], opad[64]; + for (int i = 0; i < 64; i++) { + ipad[i] = k0[i] ^ 0x36; + opad[i] = k0[i] ^ 0x5c; + } + + /* inner = H((K0 ^ ipad) || message) */ + EP_SHA256_CTX ic; + ep_sha256_init(&ic); + ep_sha256_update(&ic, ipad, 64); + if (msg && mlen) ep_sha256_update(&ic, msg, mlen); + unsigned char inner[32]; + ep_sha256_final(&ic, inner); + + /* mac = H((K0 ^ opad) || inner) */ + EP_SHA256_CTX oc; + ep_sha256_init(&oc); + ep_sha256_update(&oc, opad, 64); + ep_sha256_update(&oc, inner, 32); + unsigned char mac[32]; + ep_sha256_final(&oc, mac); + + char* out = (char*)malloc(65); + if (out) { + for (int i = 0; i < 32; i++) { + snprintf(out + (i * 2), 3, "%02x", mac[i]); + } + out[64] = '\0'; + } + return (long long)out; +} + +typedef struct { + unsigned int count[2]; + unsigned int state[4]; + unsigned char buffer[64]; +} EP_MD5_CTX; + +#define F(x,y,z) (((x) & (y)) | (~(x) & (z))) +#define G(x,y,z) (((x) & (z)) | ((y) & ~(z))) +#define H(x,y,z) ((x) ^ (y) ^ (z)) +#define I(x,y,z) ((y) ^ ((x) | ~(z))) +#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n)))) + +#define FF(a,b,c,d,x,s,ac) { \ + (a) += F((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} +#define GG(a,b,c,d,x,s,ac) { \ + (a) += G((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} +#define HH(a,b,c,d,x,s,ac) { \ + (a) += H((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} +#define II(a,b,c,d,x,s,ac) { \ + (a) += I((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} + +void ep_md5_init(EP_MD5_CTX *ctx) { + ctx->count[0] = ctx->count[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) { + unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + for (int i = 0, j = 0; i < 16; i++, j += 4) + x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24); + + FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee); + FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501); + FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be); + FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821); + + GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); + GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); + GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed); + GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); + + HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c); + HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70); + HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05); + HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665); + + II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039); + II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1); + II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1); + II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391); + + state[0] += a; state[1] += b; state[2] += c; state[3] += d; +} + +void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) { + unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index; + ctx->count[0] += input_len << 3; + if (ctx->count[0] < (input_len << 3)) ctx->count[1]++; + ctx->count[1] += input_len >> 29; + if (input_len >= part_len) { + memcpy(&ctx->buffer[index], input, part_len); + ep_md5_transform(ctx->state, ctx->buffer); + for (i = part_len; i + 63 < input_len; i += 64) + ep_md5_transform(ctx->state, &input[i]); + index = 0; + } + memcpy(&ctx->buffer[index], &input[i], input_len - i); +} + +void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) { + unsigned char bits[8]; + bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24; + bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24; + unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index); + unsigned char padding[64]; + memset(padding, 0, 64); padding[0] = 0x80; + ep_md5_update(ctx, padding, pad_len); + ep_md5_update(ctx, bits, 8); + for (int i = 0; i < 4; i++) { + digest[i*4] = ctx->state[i]; + digest[i*4 + 1] = ctx->state[i] >> 8; + digest[i*4 + 2] = ctx->state[i] >> 16; + digest[i*4 + 3] = ctx->state[i] >> 24; + } +} + +char* ep_md5(const char* s) { + if (!s) s = ""; + EP_MD5_CTX ctx; + ep_md5_init(&ctx); + ep_md5_update(&ctx, (const unsigned char*)s, strlen(s)); + unsigned char hash[16]; + ep_md5_final(&ctx, hash); + char* result = malloc(33); + if (result) { + for (int i = 0; i < 16; i++) { + snprintf(result + (i * 2), 3, "%02x", hash[i]); + } + result[32] = '\0'; + } + return result; +} + +char* read_file_content(const char* filepath) { + char mode[3]; + mode[0] = 'r'; + mode[1] = 'b'; + mode[2] = '\0'; + FILE* f = fopen(filepath, mode); + if (!f) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = malloc(size + 1); + if (!buf) { + fclose(f); + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + size_t read_bytes = fread(buf, 1, size, f); + buf[read_bytes] = '\0'; + fclose(f); + ep_gc_register(buf, EP_OBJ_STRING); + return buf; +} + +long long string_length(const char* s) { + if (!s) return 0; + return strlen(s); +} + +long long get_character(const char* s, long long index) { + if (!s) return 0; + long long len = strlen(s); + if (index < 0 || index >= len) return 0; + return (unsigned char)s[index]; +} + +long long create_list(void) { + EpList* list = malloc(sizeof(EpList)); + if (!list) return 0; + list->capacity = 4; + list->length = 0; + list->data = malloc(list->capacity * sizeof(long long)); + ep_gc_register(list, EP_OBJ_LIST); + return (long long)list; +} + +long long get_list_data_ptr(long long list_ptr) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list) return 0; + return (long long)list->data; +} + +long long append_list(long long list_ptr, long long value) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list) return 0; + if (list->length >= list->capacity) { + list->capacity *= 2; + list->data = realloc(list->data, list->capacity * sizeof(long long)); + } + list->data[list->length] = value; + list->length += 1; + ep_gc_write_barrier((void*)list_ptr, value); + return value; +} + +long long get_list(long long list_ptr, long long index) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (index < 0 || index >= list->length) return 0; + return list->data[index]; +} + +long long set_list(long long list_ptr, long long index, long long value) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (index < 0 || index >= list->length) return 0; + list->data[index] = value; + ep_gc_write_barrier((void*)list_ptr, value); + return value; +} + +long long length_list(long long list_ptr) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + return list->length; +} + +long long free_list(long long list_ptr) { + EpList* list = (EpList*)list_ptr; + if (!list) return 0; + /* Skip if already freed (idempotent) */ + if (!ep_gc_find(list)) return 0; + ep_gc_unregister(list); + free(list->data); + free(list); + return 0; +} + +static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) { + EpList* rows = (EpList*)arg; + EpList* row = (EpList*)create_list(); + for (int i = 0; i < argc; i++) { + char* val = argv[i] ? strdup(argv[i]) : strdup(""); + append_list((long long)row, (long long)val); + } + append_list((long long)rows, (long long)row); + return 0; +} + +long long sqlite_get_callback_ptr(long long dummy) { + return (long long)sqlite_list_callback; +} + +/* SQLite type-safe wrappers — marshal between int and long long */ +#ifdef EP_HAS_SQLITE +typedef struct sqlite3 sqlite3; +int sqlite3_open(const char*, sqlite3**); +int sqlite3_close(sqlite3*); +int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**); + +long long ep_sqlite3_open(long long filename, long long db_ptr) { + sqlite3* db = NULL; + int rc = sqlite3_open((const char*)filename, &db); + if (rc == 0 && db_ptr != 0) { + *((long long*)db_ptr) = (long long)db; + } + return (long long)rc; +} + +long long ep_sqlite3_close(long long db) { + return (long long)sqlite3_close((sqlite3*)db); +} + +long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) { + return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql, + (int(*)(void*,int,char**,char**))(callback), + (void*)cb_arg, (char**)errmsg_ptr); +} + +/* Prepared-statement API for parameterized queries (defeats SQL injection). */ +typedef struct sqlite3_stmt sqlite3_stmt; +int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**); +int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*)); +int sqlite3_bind_int64(sqlite3_stmt*, int, long long); +int sqlite3_step(sqlite3_stmt*); +int sqlite3_column_count(sqlite3_stmt*); +const unsigned char* sqlite3_column_text(sqlite3_stmt*, int); +long long sqlite3_column_int64(sqlite3_stmt*, int); +int sqlite3_finalize(sqlite3_stmt*); + +long long ep_sqlite3_prepare_v2(long long db, long long sql) { + sqlite3_stmt* stmt = NULL; + int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL); + if (rc != 0) return 0; + return (long long)stmt; +} + +long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) { + /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is + a bound parameter, never concatenated into SQL — this is the safe path. */ + return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx, + (const char*)value, -1, (void(*)(void*))(intptr_t)-1); +} + +long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) { + return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value); +} + +long long ep_sqlite3_step(long long stmt) { + return (long long)sqlite3_step((sqlite3_stmt*)stmt); +} + +long long ep_sqlite3_column_count(long long stmt) { + return (long long)sqlite3_column_count((sqlite3_stmt*)stmt); +} + +long long ep_sqlite3_column_text(long long stmt, long long col) { + const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col); + if (!t) return (long long)strdup(""); + return (long long)strdup((const char*)t); +} + +long long ep_sqlite3_column_int(long long stmt, long long col) { + return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col); +} + +long long ep_sqlite3_finalize(long long stmt) { + return (long long)sqlite3_finalize((sqlite3_stmt*)stmt); +} +#endif /* EP_HAS_SQLITE */ + +int ep_argc = 0; +char** ep_argv = NULL; + +void init_ep_args(int argc, char** argv) { + ep_argc = argc; + ep_argv = argv; + ep_gc_register_thread((void*)&argc); + /* Wire up channel scanning for GC (defined after EpChannel struct) */ + ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl; + ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl; + /* Wire up map value traversal for GC (defined after EpMap struct) */ + ep_gc_mark_map_values = ep_gc_mark_map_values_impl; + ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl; +} + +long long get_argument_count(void) { + return ep_argc; +} + +const char* get_argument(long long index) { + if (index < 0 || index >= ep_argc) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + return empty; + } + return ep_argv[index]; +} + +long long write_file_content(const char* filepath, const char* content) { + char mode[3]; + mode[0] = 'w'; + mode[1] = 'b'; + mode[2] = '\0'; + FILE* f = fopen(filepath, mode); + if (!f) return 0; + size_t len = strlen(content); + size_t written = fwrite(content, 1, len, f); + fclose(f); + return written == len ? 1 : 0; +} + +long long run_command(const char* command) { + if (!command) return -1; + return system(command); +} + +char* substring(const char* s, long long start, long long len) { + if (!s) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + long long total_len = strlen(s); + if (start < 0 || start >= total_len || len <= 0) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + if (start + len > total_len) { + len = total_len - start; + } + char* sub = malloc(len + 1); + if (!sub) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + strncpy(sub, s + start, len); + sub[len] = '\0'; + ep_gc_register(sub, EP_OBJ_STRING); + return sub; +} + +char* string_from_list(long long list_ptr) { + EpList* list = (EpList*)list_ptr; + if (!list) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + char* s = malloc(list->length + 1); + if (!s) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + for (long long i = 0; i < list->length; i++) { + s[i] = (char)list->data[i]; + } + s[list->length] = '\0'; + ep_gc_register(s, EP_OBJ_STRING); + return s; +} + +// Inverse of string_from_list: convert a string to a list of its byte values in +// a single O(n) pass (one strlen + one copy). This lets callers iterate a string +// in O(n) total via O(1) get_list, instead of O(n) get_character per index +// (which is O(n^2) over the whole string). +long long string_to_list(const char* s) { + EpList* list = malloc(sizeof(EpList)); + if (!list) return 0; + long long len = s ? (long long)strlen(s) : 0; + list->capacity = len > 0 ? len : 4; + list->length = len; + list->data = malloc(list->capacity * sizeof(long long)); + if (!list->data) { + list->capacity = 0; + list->length = 0; + ep_gc_register(list, EP_OBJ_LIST); + return (long long)list; + } + for (long long i = 0; i < len; i++) { + list->data[i] = (unsigned char)s[i]; + } + ep_gc_register(list, EP_OBJ_LIST); + return (long long)list; +} + +long long pop_list(long long list_ptr) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list || list->length <= 0) return 0; + list->length -= 1; + return list->data[list->length]; +} + +long long remove_list(long long list_ptr, long long index) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list || index < 0 || index >= list->length) return 0; + long long removed = list->data[index]; + for (long long i = index; i < list->length - 1; i++) { + list->data[i] = list->data[i + 1]; + } + list->length -= 1; + return removed; +} + +long long display_string(const char* s) { + if (s) puts(s); + return 0; +} + +/* ========== File System Runtime ========== */ +#include +#ifdef _WIN32 + #include + #include + #define mkdir(p, m) _mkdir(p) + #define rmdir _rmdir + #define getcwd _getcwd + #define popen _popen + #define pclose _pclose + #define getpid _getpid + #define setenv(k, v, o) _putenv_s(k, v) + /* Minimal dirent polyfill for Windows */ + #include + typedef struct { char d_name[260]; } ep_dirent; + typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR; + static EP_DIR* ep_opendir(const char* p) { + EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR)); + char buf[270]; snprintf(buf, sizeof(buf), "%s\\*", p); + d->hFind = FindFirstFileA(buf, &d->data); + d->first = 1; + return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d; + } + static ep_dirent* ep_readdir(EP_DIR* d) { + static ep_dirent ent; + if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; } + if (!FindNextFileA(d->hFind, &d->data)) return NULL; + strcpy(ent.d_name, d->data.cFileName); return &ent; + } + static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); } + #define DIR EP_DIR + #define dirent ep_dirent + #define opendir ep_opendir + #define readdir ep_readdir + #define closedir ep_closedir +#else + #include + #include +#endif + +long long ep_read_file(long long path_ptr) { + const char* path = (const char*)path_ptr; + FILE* f = fopen(path, "rb"); + if (!f) return (long long)""; + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = (char*)malloc(size + 1); + fread(buf, 1, size, f); + buf[size] = '\0'; + fclose(f); + return (long long)buf; +} + +long long ep_write_file(long long path_ptr, long long content_ptr) { + const char* path = (const char*)path_ptr; + const char* content = (const char*)content_ptr; + FILE* f = fopen(path, "wb"); + if (!f) return 0; + fputs(content, f); + fclose(f); + return 1; +} + +long long ep_append_file(long long path_ptr, long long content_ptr) { + const char* path = (const char*)path_ptr; + const char* content = (const char*)content_ptr; + FILE* f = fopen(path, "ab"); + if (!f) return 0; + fputs(content, f); + fclose(f); + return 1; +} + +long long ep_file_exists(long long path_ptr) { + const char* path = (const char*)path_ptr; + struct stat st; + return stat(path, &st) == 0 ? 1 : 0; +} + +long long ep_is_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + struct stat st; + if (stat(path, &st) != 0) return 0; + return S_ISDIR(st.st_mode) ? 1 : 0; +} + +long long ep_file_size(long long path_ptr) { + const char* path = (const char*)path_ptr; + struct stat st; + if (stat(path, &st) != 0) return -1; + return (long long)st.st_size; +} + +long long ep_list_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + DIR* dir = opendir(path); + if (!dir) return (long long)create_list(); + long long list = create_list(); + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || + (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) continue; + char* name = strdup(entry->d_name); + append_list(list, (long long)name); + } + closedir(dir); + return list; +} + +long long ep_create_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + return mkdir(path, 0755) == 0 ? 1 : 0; +} + +long long ep_remove_file(long long path_ptr) { + const char* path = (const char*)path_ptr; + return remove(path) == 0 ? 1 : 0; +} + +long long ep_remove_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + return rmdir(path) == 0 ? 1 : 0; +} + +long long ep_rename_file(long long old_ptr, long long new_ptr) { + return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0; +} + +long long ep_copy_file(long long src_ptr, long long dst_ptr) { + const char* src = (const char*)src_ptr; + const char* dst = (const char*)dst_ptr; + FILE* fin = fopen(src, "rb"); + if (!fin) return 0; + FILE* fout = fopen(dst, "wb"); + if (!fout) { fclose(fin); return 0; } + char buf[8192]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) { + fwrite(buf, 1, n, fout); + } + fclose(fin); + fclose(fout); + return 1; +} + +/* ========== Date/Time Runtime ========== */ +#include +#include + +long long ep_time_now_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL; +} + +long long ep_time_now_sec(void) { + return (long long)time(NULL); +} + + +long long ep_time_year(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_year + 1900 : 0; +} + +long long ep_time_month(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_mon + 1 : 0; +} + +long long ep_time_day(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_mday : 0; +} + +long long ep_time_hour(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_hour : 0; +} + +long long ep_time_minute(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_min : 0; +} + +long long ep_time_second(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_sec : 0; +} + +long long ep_time_weekday(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_wday : 0; +} + +long long ep_format_time(long long ts, long long fmt_ptr) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + if (!tm) return (long long)""; + char* buf = (char*)malloc(256); + strftime(buf, 256, (const char*)fmt_ptr, tm); + return (long long)buf; +} + +/* ========== OS Runtime ========== */ + +long long ep_getenv(long long name_ptr) { + const char* val = getenv((const char*)name_ptr); + return val ? (long long)val : (long long)""; +} + +long long ep_setenv(long long name_ptr, long long val_ptr) { + return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0; +} + +long long ep_get_cwd(void) { + char* buf = (char*)malloc(4096); + if (getcwd(buf, 4096)) return (long long)buf; + free(buf); + return (long long)""; +} + +long long ep_os_name(void) { + #if defined(__APPLE__) + return (long long)"macos"; + #elif defined(__linux__) + return (long long)"linux"; + #elif defined(_WIN32) + return (long long)"windows"; + #else + return (long long)"unknown"; + #endif +} + +long long ep_arch_name(void) { + #if defined(__aarch64__) || defined(__arm64__) + return (long long)"arm64"; + #elif defined(__x86_64__) + return (long long)"x86_64"; + #elif defined(__i386__) + return (long long)"x86"; + #else + return (long long)"unknown"; + #endif +} + +long long ep_exit(long long code) { + exit((int)code); + return 0; +} + +long long ep_get_pid(void) { + return (long long)getpid(); +} + +long long ep_get_home_dir(void) { + const char* home = getenv("HOME"); + return home ? (long long)home : (long long)""; +} + +#ifdef __wasm__ +long long ep_run_command(long long cmd_ptr) { + (void)cmd_ptr; + return (long long)"Error: running external commands is not supported on WebAssembly"; +} +#else +long long ep_run_command(long long cmd_ptr) { + const char* cmd = (const char*)cmd_ptr; + FILE* fp = popen(cmd, "r"); + if (!fp) return (long long)""; + char* result = (char*)malloc(65536); + size_t total = 0; + char buf[4096]; + while (fgets(buf, sizeof(buf), fp)) { + size_t len = strlen(buf); + memcpy(result + total, buf, len); + total += len; + } + result[total] = '\0'; + pclose(fp); + return (long long)result; +} +#endif + +/* ========== HashMap helpers ========== */ + +long long ep_hash_string(long long s_ptr) { + const char* s = (const char*)s_ptr; + if (!s) return 0; + unsigned long long hash = 5381; + int c; + while ((c = *s++)) { + hash = ((hash << 5) + hash) + c; + } + return (long long)hash; +} + +long long ep_str_equals(long long a_ptr, long long b_ptr) { + if (a_ptr == b_ptr) return 1; + if (!a_ptr || !b_ptr) return 0; + /* If either value looks like a small integer (not a valid heap pointer), + fall back to integer comparison — strcmp would segfault. */ + if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0; + return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0; +} + +/* ========== Sync Primitives ========== */ + +#ifdef _WIN32 +long long ep_mutex_create(void) { + CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection(m); + return (long long)m; +} +long long ep_mutex_lock_fn(long long m) { + EnterCriticalSection((CRITICAL_SECTION*)m); + return 1; +} +long long ep_mutex_unlock_fn(long long m) { + LeaveCriticalSection((CRITICAL_SECTION*)m); + return 1; +} +long long ep_mutex_trylock(long long m) { + return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0; +} +long long ep_mutex_destroy(long long m) { + DeleteCriticalSection((CRITICAL_SECTION*)m); + free((void*)m); + return 0; +} +#else +long long ep_mutex_create(void) { + pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m, NULL); + return (long long)m; +} + +long long ep_mutex_lock_fn(long long m) { + return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_mutex_unlock_fn(long long m) { + return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_mutex_trylock(long long m) { + return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_mutex_destroy(long long m) { + pthread_mutex_destroy((pthread_mutex_t*)m); + free((void*)m); + return 0; +} +#endif + +#ifdef _WIN32 +long long ep_rwlock_create(void) { + SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK)); + InitializeSRWLock(rwl); + return (long long)rwl; +} +long long ep_rwlock_read_lock(long long rwl) { + AcquireSRWLockShared((SRWLOCK*)rwl); + return 1; +} +long long ep_rwlock_write_lock(long long rwl) { + AcquireSRWLockExclusive((SRWLOCK*)rwl); + return 1; +} +long long ep_rwlock_unlock(long long rwl) { + /* SRWLOCK does not have a single "unlock" — we try exclusive first. + In practice the caller should know which lock was taken. + ReleaseSRWLockExclusive on a shared lock is undefined, but + the runtime guarantees matched lock/unlock pairs. We default + to releasing the exclusive lock; shared unlock is handled + by pairing read_lock -> read_unlock if needed later. */ + ReleaseSRWLockExclusive((SRWLOCK*)rwl); + return 1; +} +long long ep_rwlock_destroy(long long rwl) { + /* SRWLOCK has no destroy */ + free((void*)rwl); + return 0; +} +#else +long long ep_rwlock_create(void) { + pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t)); + pthread_rwlock_init(rwl, NULL); + return (long long)rwl; +} + +long long ep_rwlock_read_lock(long long rwl) { + return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; +} + +long long ep_rwlock_write_lock(long long rwl) { + return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; +} + +long long ep_rwlock_unlock(long long rwl) { + return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; +} + +long long ep_rwlock_destroy(long long rwl) { + pthread_rwlock_destroy((pthread_rwlock_t*)rwl); + free((void*)rwl); + return 0; +} +#endif + +#ifdef _MSC_VER +long long ep_atomic_create(long long initial) { + volatile long long* a = (volatile long long*)malloc(sizeof(long long)); + InterlockedExchange64(a, initial); + return (long long)a; +} +long long ep_atomic_load(long long a) { + return InterlockedCompareExchange64((volatile long long*)a, 0, 0); +} +long long ep_atomic_store(long long a, long long value) { + InterlockedExchange64((volatile long long*)a, value); + return value; +} +long long ep_atomic_add(long long a, long long delta) { + return InterlockedExchangeAdd64((volatile long long*)a, delta); +} +long long ep_atomic_sub(long long a, long long delta) { + return InterlockedExchangeAdd64((volatile long long*)a, -delta); +} +long long ep_atomic_cas(long long a, long long expected, long long desired) { + long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected); + return (old == expected) ? 1 : 0; +} +#else +long long ep_atomic_create(long long initial) { + long long* a = (long long*)malloc(sizeof(long long)); + __atomic_store_n(a, initial, __ATOMIC_SEQ_CST); + return (long long)a; +} + +long long ep_atomic_load(long long a) { + return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST); +} + +long long ep_atomic_store(long long a, long long value) { + __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST); + return value; +} + +long long ep_atomic_add(long long a, long long delta) { + return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST); +} + +long long ep_atomic_sub(long long a, long long delta) { + return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST); +} + +long long ep_atomic_cas(long long a, long long expected, long long desired) { + long long exp = expected; + return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0; +} +#endif + +/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */ +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + unsigned count; + unsigned target; + unsigned generation; +} EpBarrier; + +long long ep_barrier_create(long long count) { + EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier)); + pthread_mutex_init(&b->mutex, NULL); + pthread_cond_init(&b->cond, NULL); + b->count = 0; + b->target = (unsigned)count; + b->generation = 0; + return (long long)b; +} + +long long ep_barrier_wait(long long bp) { + EpBarrier* b = (EpBarrier*)bp; + pthread_mutex_lock(&b->mutex); + unsigned gen = b->generation; + b->count++; + if (b->count >= b->target) { + b->count = 0; + b->generation++; + pthread_cond_broadcast(&b->cond); + pthread_mutex_unlock(&b->mutex); + return 1; /* serial thread */ + } + while (gen == b->generation) { + pthread_cond_wait(&b->cond, &b->mutex); + } + pthread_mutex_unlock(&b->mutex); + return 0; +} + +long long ep_barrier_destroy(long long bp) { + EpBarrier* b = (EpBarrier*)bp; + pthread_mutex_destroy(&b->mutex); + pthread_cond_destroy(&b->cond); + free(b); + return 0; +} + +/* Semaphore via mutex+condvar (portable) */ +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + long long value; +} EpSemaphore; + +long long ep_semaphore_create(long long initial) { + EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore)); + pthread_mutex_init(&s->mutex, NULL); + pthread_cond_init(&s->cond, NULL); + s->value = initial; + return (long long)s; +} + +long long ep_semaphore_wait(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_lock(&s->mutex); + while (s->value <= 0) { + pthread_cond_wait(&s->cond, &s->mutex); + } + s->value--; + pthread_mutex_unlock(&s->mutex); + return 1; +} + +long long ep_semaphore_post(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_lock(&s->mutex); + s->value++; + pthread_cond_signal(&s->cond); + pthread_mutex_unlock(&s->mutex); + return 1; +} + +long long ep_semaphore_trywait(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_lock(&s->mutex); + if (s->value > 0) { + s->value--; + pthread_mutex_unlock(&s->mutex); + return 1; + } + pthread_mutex_unlock(&s->mutex); + return 0; +} + +long long ep_semaphore_destroy(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_destroy(&s->mutex); + pthread_cond_destroy(&s->cond); + free(s); + return 0; +} + +long long ep_condvar_create(void) { + pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t)); + pthread_cond_init(cv, NULL); + return (long long)cv; +} + +long long ep_condvar_wait(long long cv, long long m) { + return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_condvar_signal(long long cv) { + return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0; +} + +long long ep_condvar_broadcast(long long cv) { + return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0; +} + +long long ep_condvar_destroy(long long cv) { + pthread_cond_destroy((pthread_cond_t*)cv); + free((void*)cv); + return 0; +} + +/* ========== Regex (simple stub — delegates to POSIX regex) ========== */ +#include + +long long ep_regex_match(long long pattern_ptr, long long text_ptr) { + regex_t regex; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB); + if (ret) return 0; + ret = regexec(®ex, text, 0, NULL, 0); + regfree(®ex); + return ret == 0 ? 1 : 0; +} + +long long ep_regex_find(long long pattern_ptr, long long text_ptr) { + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) return (long long)""; + ret = regexec(®ex, text, 1, &match, 0); + if (ret != 0) { regfree(®ex); return (long long)""; } + int len = match.rm_eo - match.rm_so; + char* result = (char*)malloc(len + 1); + memcpy(result, text + match.rm_so, len); + result[len] = '\0'; + regfree(®ex); + return (long long)result; +} + +long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) { + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + long long list = create_list(); + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) return list; + const char* cursor = text; + while (regexec(®ex, cursor, 1, &match, 0) == 0) { + int len = match.rm_eo - match.rm_so; + char* result = (char*)malloc(len + 1); + memcpy(result, cursor + match.rm_so, len); + result[len] = '\0'; + append_list(list, (long long)result); + cursor += match.rm_eo; + if (match.rm_eo == 0) break; + } + regfree(®ex); + return list; +} + +long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) { + /* Simple single-replacement via regex */ + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + const char* repl = (const char*)repl_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) return text_ptr; + ret = regexec(®ex, text, 1, &match, 0); + if (ret != 0) { regfree(®ex); return text_ptr; } + size_t tlen = strlen(text); + size_t rlen = strlen(repl); + size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen; + char* result = (char*)malloc(new_len + 1); + memcpy(result, text, match.rm_so); + memcpy(result + match.rm_so, repl, rlen); + memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo); + result[new_len] = '\0'; + regfree(®ex); + return (long long)result; +} + +long long ep_regex_split(long long pattern_ptr, long long text_ptr) { + long long list = create_list(); + /* Simple split: find matches and split around them */ + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) { + append_list(list, text_ptr); + return list; + } + const char* cursor = text; + while (regexec(®ex, cursor, 1, &match, 0) == 0) { + int len = match.rm_so; + char* part = (char*)malloc(len + 1); + memcpy(part, cursor, len); + part[len] = '\0'; + append_list(list, (long long)part); + cursor += match.rm_eo; + if (match.rm_eo == 0) break; + } + char* rest = strdup(cursor); + append_list(list, (long long)rest); + regfree(®ex); + return list; +} + +/* ========== Base64 ========== */ +static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +long long ep_base64_encode(long long data_ptr) { + const unsigned char* data = (const unsigned char*)data_ptr; + size_t len = strlen((const char*)data); + size_t out_len = 4 * ((len + 2) / 3); + char* out = (char*)malloc(out_len + 1); + size_t i, j = 0; + for (i = 0; i < len; i += 3) { + unsigned int n = data[i] << 16; + if (i + 1 < len) n |= data[i+1] << 8; + if (i + 2 < len) n |= data[i+2]; + out[j++] = b64_table[(n >> 18) & 63]; + out[j++] = b64_table[(n >> 12) & 63]; + out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '='; + out[j++] = (i + 2 < len) ? b64_table[n & 63] : '='; + } + out[j] = '\0'; + return (long long)out; +} + +long long ep_uuid_v4(void) { + char* uuid = (char*)malloc(37); + unsigned char bytes[16]; + ep_secure_random_bytes(bytes, 16); + bytes[6] = (bytes[6] & 0x0F) | 0x40; + bytes[8] = (bytes[8] & 0x3F) | 0x80; + snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], + bytes[12], bytes[13], bytes[14], bytes[15]); + return (long long)uuid; +} + +long long file_read(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return (long long)strdup(""); + FILE* f = fopen(path, "rb"); + if (!f) return (long long)strdup(""); + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = malloc(size + 1); + if (!buf) { fclose(f); return (long long)strdup(""); } + fread(buf, 1, size, f); + buf[size] = '\0'; + fclose(f); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + +long long file_write(long long path_val, long long content_val) { + const char* path = (const char*)path_val; + const char* content = (const char*)content_val; + if (!path || !content) return 0; + FILE* f = fopen(path, "wb"); + if (!f) return 0; + size_t len = strlen(content); + fwrite(content, 1, len, f); + fclose(f); + return 1; +} + +long long file_append(long long path_val, long long content_val) { + const char* path = (const char*)path_val; + const char* content = (const char*)content_val; + if (!path || !content) return 0; + FILE* f = fopen(path, "ab"); + if (!f) return 0; + size_t len = strlen(content); + fwrite(content, 1, len, f); + fclose(f); + return 1; +} + +long long file_exists(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + FILE* f = fopen(path, "r"); + if (f) { fclose(f); return 1; } + return 0; +} + +long long string_contains(long long s_val, long long sub_val) { + const char* s = (const char*)s_val; + const char* sub = (const char*)sub_val; + if (!s || !sub) return 0; + return strstr(s, sub) != NULL ? 1 : 0; +} + +long long string_index_of(long long s_val, long long sub_val) { + const char* s = (const char*)s_val; + const char* sub = (const char*)sub_val; + if (!s || !sub) return -1; + const char* found = strstr(s, sub); + if (!found) return -1; + return (long long)(found - s); +} + +long long string_replace(long long s_val, long long old_val, long long new_val) { + const char* s = (const char*)s_val; + const char* old_str = (const char*)old_val; + const char* new_str = (const char*)new_val; + if (!s || !old_str || !new_str) return (long long)strdup(s ? s : ""); + size_t old_len = strlen(old_str); + size_t new_len = strlen(new_str); + if (old_len == 0) return (long long)strdup(s); + int count = 0; + const char* p = s; + while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; } + size_t result_len = strlen(s) + count * (new_len - old_len); + char* result = malloc(result_len + 1); + if (!result) return (long long)strdup(s); + char* dst = result; + p = s; + while (*p) { + if (strncmp(p, old_str, old_len) == 0) { + memcpy(dst, new_str, new_len); + dst += new_len; + p += old_len; + } else { + *dst++ = *p++; + } + } + *dst = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +/* ========== Additional String Functions ========== */ +#include + +long long string_upper(long long s_val) { + const char* s = (const char*)s_val; + if (!s) return (long long)strdup(""); + long long len = strlen(s); + char* result = malloc(len + 1); + for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]); + result[len] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long string_lower(long long s_val) { + const char* s = (const char*)s_val; + if (!s) return (long long)strdup(""); + long long len = strlen(s); + char* result = malloc(len + 1); + for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]); + result[len] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long string_trim(long long s_val) { + const char* s = (const char*)s_val; + if (!s) return (long long)strdup(""); + while (*s && isspace((unsigned char)*s)) s++; + long long len = strlen(s); + while (len > 0 && isspace((unsigned char)s[len - 1])) len--; + char* result = malloc(len + 1); + memcpy(result, s, len); + result[len] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long string_split(long long s_val, long long delim_val) { + const char* s = (const char*)s_val; + const char* delim = (const char*)delim_val; + if (!s || !delim) return create_list(); + long long list = create_list(); + long long dlen = strlen(delim); + if (dlen == 0) { append_list(list, s_val); return list; } + const char* p = s; + while (1) { + const char* found = strstr(p, delim); + long long partlen = found ? (found - p) : (long long)strlen(p); + char* part = malloc(partlen + 1); + memcpy(part, p, partlen); + part[partlen] = '\0'; + ep_gc_register(part, EP_OBJ_STRING); + append_list(list, (long long)part); + if (!found) break; + p = found + dlen; + } + return list; +} + +long long char_at(long long s_val, long long index) { + const char* s = (const char*)s_val; + if (!s || index < 0 || index >= (long long)strlen(s)) return 0; + return (unsigned char)s[index]; +} + +long long char_from_code(long long code) { + char* result = malloc(2); + result[0] = (char)code; + result[1] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long ep_abs(long long n) { + return n < 0 ? -n : n; +} + +// Auto-convert any value to string for string interpolation +long long ep_auto_to_string(long long val) { + // If the value is 0, return "0" + if (val == 0) return (long long)strdup("0"); + // Check if val is a GC-tracked string (heap-allocated) + EpGCObject* obj = ep_gc_find((void*)val); + if (obj && obj->kind == EP_OBJ_STRING) { + return val; // It's a known string pointer + } + // Check if val is a static string literal (in .rodata/.data segment) + // These aren't GC-tracked but ARE valid pointers. Use a safe probe: + // only dereference if the address is in a readable memory page. + if (val > 0x100000) { +#if defined(_WIN32) + // Windows: use VirtualQuery to safely probe pointer validity + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) { + const char* p = (const char*)(void*)val; + unsigned char first = (unsigned char)*p; + if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { + return val; // Readable memory, looks like a string + } + } +#elif defined(__APPLE__) + // macOS: use vm_read_overwrite to safely probe + char probe; + vm_size_t sz = 1; + kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz); + if (kr == KERN_SUCCESS) { + unsigned char first = (unsigned char)probe; + if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { + return val; // Readable memory, looks like a string + } + } +#else + // Linux: use write() to /dev/null as a safe pointer probe + // write() returns -1 with EFAULT for invalid pointers, no signal + int devnull = open("/dev/null", 1); // O_WRONLY + if (devnull >= 0) { + ssize_t r = write(devnull, (const void*)val, 1); + close(devnull); + if (r == 1) { + const char* p = (const char*)(void*)val; + unsigned char first = (unsigned char)*p; + if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { + return val; + } + } + } +#endif + } + // Otherwise, convert integer to string + char* buf = (char*)malloc(32); + snprintf(buf, 32, "%lld", val); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + +long long ep_random_int(long long min, long long max) { + if (max <= min) return min; + /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */ + unsigned long long range = (unsigned long long)(max - min) + 1ULL; + unsigned long long limit = UINT64_MAX - (UINT64_MAX % range); + unsigned long long r; + do { + ep_secure_random_bytes((unsigned char*)&r, sizeof(r)); + } while (r >= limit); + return min + (long long)(r % range); +} + +// JSON built-in functions +static const char* json_skip_ws(const char* p) { + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + return p; +} + +static const char* json_skip_value(const char* p) { + p = json_skip_ws(p); + if (*p == '"') { + p++; + while (*p && *p != '"') { if (*p == '\\') p++; p++; } + if (*p == '"') p++; + } else if (*p == '{') { + int depth = 1; p++; + while (*p && depth > 0) { + if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; } + else if (*p == '{') { depth++; p++; } + else if (*p == '}') { depth--; p++; } + else p++; + } + } else if (*p == '[') { + int depth = 1; p++; + while (*p && depth > 0) { + if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; } + else if (*p == '[') { depth++; p++; } + else if (*p == ']') { depth--; p++; } + else p++; + } + } else { + while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\n') p++; + } + return p; +} + +static const char* json_find_key(const char* json, const char* key) { + const char* p = json_skip_ws(json); + if (*p != '{') return NULL; + p++; + while (*p) { + p = json_skip_ws(p); + if (*p == '}') return NULL; + if (*p != '"') return NULL; + p++; + const char* ks = p; + while (*p && *p != '"') { if (*p == '\\') p++; p++; } + size_t klen = p - ks; + if (*p == '"') p++; + p = json_skip_ws(p); + if (*p == ':') p++; + p = json_skip_ws(p); + if (klen == strlen(key) && strncmp(ks, key, klen) == 0) { + return p; + } + p = json_skip_value(p); + p = json_skip_ws(p); + if (*p == ',') p++; + } + return NULL; +} + +long long json_get_string(long long json_val, long long key_val) { + const char* json = (const char*)json_val; + const char* key = (const char*)key_val; + if (!json || !key) return (long long)strdup(""); + const char* val = json_find_key(json, key); + if (!val || *val != '"') return (long long)strdup(""); + val++; + const char* end = val; + while (*end && *end != '"') { if (*end == '\\') end++; end++; } + size_t len = end - val; + char* result = (char*)malloc(len + 1); + // Handle escape sequences + size_t di = 0; + const char* si = val; + while (si < end) { + if (*si == '\\' && si + 1 < end) { + si++; + switch (*si) { + case 'n': result[di++] = '\n'; break; + case 't': result[di++] = '\t'; break; + case 'r': result[di++] = '\r'; break; + case '"': result[di++] = '"'; break; + case '\\': result[di++] = '\\'; break; + default: result[di++] = *si; break; + } + } else { + result[di++] = *si; + } + si++; + } + result[di] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long json_get_int(long long json_val, long long key_val) { + const char* json = (const char*)json_val; + const char* key = (const char*)key_val; + if (!json || !key) return 0; + const char* val = json_find_key(json, key); + if (!val) return 0; + return atoll(val); +} + +long long json_get_bool(long long json_val, long long key_val) { + const char* json = (const char*)json_val; + const char* key = (const char*)key_val; + if (!json || !key) return 0; + const char* val = json_find_key(json, key); + if (!val) return 0; + if (strncmp(val, "true", 4) == 0) return 1; + return 0; +} + +// SHA-1 implementation (RFC 3174) for WebSocket handshake +static unsigned int sha1_left_rotate(unsigned int x, int n) { + return (x << n) | (x >> (32 - n)); +} + +long long ep_sha1(long long data_val) { + const unsigned char* data = (const unsigned char*)data_val; + if (!data) return (long long)strdup(""); + size_t len = strlen((const char*)data); + + unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0; + size_t new_len = len + 1; + while (new_len % 64 != 56) new_len++; + unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1); + memcpy(msg, data, len); + msg[len] = 0x80; + unsigned long long bits_len = (unsigned long long)len * 8; + for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8)); + + for (size_t offset = 0; offset < new_len + 8; offset += 64) { + unsigned int w[80]; + for (int i = 0; i < 16; i++) { + w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) | + ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3]; + } + for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1); + unsigned int a = h0, b = h1, c = h2, d = h3, e = h4; + for (int i = 0; i < 80; i++) { + unsigned int f, k; + if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; } + else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; } + else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; } + else { f = b ^ c ^ d; k = 0xCA62C1D6; } + unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i]; + e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp; + } + h0 += a; h1 += b; h2 += c; h3 += d; h4 += e; + } + free(msg); + + // Return Base64-encoded hash directly (for WebSocket handshake) + unsigned char hash[20]; + hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF; + hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF; + hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF; + hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF; + hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF; + + // Base64 encode the 20-byte hash + size_t b64_len = 4 * ((20 + 2) / 3); + char* result = (char*)malloc(b64_len + 1); + size_t j = 0; + for (size_t bi = 0; bi < 20; bi += 3) { + unsigned int n2 = ((unsigned int)hash[bi]) << 16; + if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8; + if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2]; + result[j++] = b64_table[(n2 >> 18) & 0x3F]; + result[j++] = b64_table[(n2 >> 12) & 0x3F]; + result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '='; + result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '='; + } + result[j] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +// Read exact N bytes from a socket +#ifdef __wasm__ +long long ep_net_recv_bytes(long long fd, long long count) { + (void)fd; (void)count; + return (long long)strdup(""); +} +#else +long long ep_net_recv_bytes(long long fd, long long count) { + if (count <= 0) return (long long)strdup(""); + char* buf = (char*)malloc(count + 1); +#ifdef _WIN32 + int total = 0; + while (total < (int)count) { + int n = recv((int)fd, buf + total, (int)(count - total), 0); + if (n <= 0) break; + total += n; + } +#else + ssize_t total = 0; + while (total < count) { + ssize_t n = recv((int)fd, buf + total, count - total, 0); + if (n <= 0) break; + total += n; + } +#endif + buf[total] = '\0'; + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} +#endif + +long long ep_get_args(void) { + long long list_ptr = create_list(); + for (int i = 0; i < ep_argc; i++) { + char* arg_copy = strdup(ep_argv[i]); + ep_gc_register(arg_copy, EP_OBJ_STRING); + append_list(list_ptr, (long long)arg_copy); + } + return list_ptr; +} + diff --git a/src/codegen.rs b/src/codegen.rs index fb8ce9a..60051db 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -3557,7 +3557,10 @@ int main(int argc, char** argv) {{ self.out.push_str(&format!(" long long {};\n", Self::sanitize_c_name(¶m.0))); } // Local variables: - for (var_name, _) in &var_types { + // Sorted iteration: HashMap order is nondeterministic and would make the + // emitted C differ run-to-run (breaks reproducible builds + the + // byte-identical parity gate between ernos and epc). + for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } { let is_param = func.params.iter().any(|p| &p.0 == var_name); let is_global = self.global_constants.contains(var_name); if !is_param && !is_global { @@ -3706,7 +3709,10 @@ int main(int argc, char** argv) {{ scan_stmts_for_borrows(&func.body, &mut borrowed_vars); - for (var_name, _) in &var_types { + // Sorted iteration: HashMap order is nondeterministic and would make the + // emitted C differ run-to-run (breaks reproducible builds + the + // byte-identical parity gate between ernos and epc). + for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } { let is_param = func.params.iter().any(|p| &p.0 == var_name); let is_global = self.global_constants.contains(var_name); if !is_param && !is_global { @@ -3718,7 +3724,10 @@ int main(int argc, char** argv) {{ // Push GC roots for all locals let mut gc_root_count = 0; - for (var_name, _) in &var_types { + // Sorted iteration: HashMap order is nondeterministic and would make the + // emitted C differ run-to-run (breaks reproducible builds + the + // byte-identical parity gate between ernos and epc). + for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } { let is_param = func.params.iter().any(|p| &p.0 == var_name); let is_global = self.global_constants.contains(var_name); if !is_param && !is_global { @@ -3787,7 +3796,10 @@ int main(int argc, char** argv) {{ // Get the function's return type to avoid freeing the returned value let func_ret_type = self.func_return_types.get(&func.name).cloned(); - for (var_name, _) in &var_types { + // Sorted iteration: HashMap order is nondeterministic and would make the + // emitted C differ run-to-run (breaks reproducible builds + the + // byte-identical parity gate between ernos and epc). + for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } { let is_param = func.params.iter().any(|p| &p.0 == var_name); let is_global = self.global_constants.contains(var_name); let is_borrowed = borrowed_vars.contains(var_name); @@ -3991,7 +4003,10 @@ int main(int argc, char** argv) {{ self.out.push_str(&format!("long long {}__{}({}) {{\n", md.struct_name, md.name, params_decl.join(", "))); - for (var_name, _) in &var_types { + // Sorted iteration: HashMap order is nondeterministic and would make the + // emitted C differ run-to-run (breaks reproducible builds + the + // byte-identical parity gate between ernos and epc). + for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } { let is_param = var_name == "self" || md.params.iter().any(|p| &p.0 == var_name); if !is_param { let safe_var = Self::sanitize_c_name(var_name); @@ -4002,7 +4017,10 @@ int main(int argc, char** argv) {{ // Push GC roots for all locals let mut gc_root_count = 0; - for (var_name, _) in &var_types { + // Sorted iteration: HashMap order is nondeterministic and would make the + // emitted C differ run-to-run (breaks reproducible builds + the + // byte-identical parity gate between ernos and epc). + for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } { let is_param = var_name == "self" || md.params.iter().any(|p| &p.0 == var_name); if !is_param { let t = var_types.get(var_name); @@ -4076,4715 +4094,7 @@ int main(int argc, char** argv) {{ } } -const RUNTIME_HEADER_AND_SRC: &str = r#"#include -#include -#include -#include -#ifdef __wasm__ -#define _SETJMP_H -typedef int jmp_buf[1]; -#define setjmp(buf) (0) -#define longjmp(buf, val) abort() - -// Mock pthreads for single-threaded WASM -typedef struct { int lock_state; } pthread_mutex_t; -typedef struct { int cond_state; } pthread_cond_t; -typedef struct { int rw_state; } pthread_rwlock_t; -typedef int pthread_t; -typedef int pthread_attr_t; -#define PTHREAD_MUTEX_INITIALIZER {0} -#define PTHREAD_COND_INITIALIZER {0} -#define PTHREAD_RWLOCK_INITIALIZER {0} -#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0) -#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0) -#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0) -#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1) -#define pthread_mutex_destroy(m) ((void)(m), 0) -#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0) -#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0) -#define pthread_cond_signal(c) ((void)(c), 0) -#define pthread_cond_broadcast(c) ((void)(c), 0) -#define pthread_cond_destroy(c) ((void)(c), 0) -#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0) -#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0) -#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0) -#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0) -#define pthread_rwlock_destroy(r) ((void)(r), 0) -#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0) -#define pthread_join(t, r) ((void)(t), (void)(r), 0) -#define pthread_detach(t) ((void)(t), 0) -#else -#include -#endif -#include -#include -#ifndef _WIN32 -#include -#endif -#if defined(__APPLE__) -#include -#endif -#if defined(__linux__) -#include -#endif -#include - -/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on - Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a - /dev/urandom read elsewhere. Only if all of those are unavailable does it - fall back to rand() — never on a supported platform. */ -static void ep_secure_random_bytes(unsigned char* buf, size_t n) { -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - arc4random_buf(buf, n); -#else - size_t got = 0; - #if defined(__linux__) - while (got < n) { - ssize_t r = getrandom(buf + got, n - got, 0); - if (r <= 0) break; - got += (size_t)r; - } - #endif - if (got < n) { - FILE* f = fopen("/dev/urandom", "rb"); - if (f) { - got += fread(buf + got, 1, n - got, f); - fclose(f); - } - } - while (got < n) { - buf[got++] = (unsigned char)(rand() & 0xFF); - } -#endif -} - -/* Try/catch infrastructure */ -static jmp_buf ep_try_buf; -static volatile int ep_try_active = 0; - -static void ep_signal_handler(int sig) { - if (ep_try_active) { - ep_try_active = 0; - longjmp(ep_try_buf, sig); - } - /* Outside try: print error and exit */ - const char* name = sig == SIGSEGV ? "segmentation fault (null pointer or invalid memory access)" - : sig == SIGFPE ? "arithmetic error (division by zero)" - : sig == SIGABRT ? "aborted" - : "unknown signal"; - fprintf(stderr, "\nRuntime Error: %s (signal %d)\n", name, sig); - - /* Write to daemon/general log file if environment variable is set */ - const char* daemon_log = getenv("ERNOS_DAEMON_LOG"); - if (!daemon_log || daemon_log[0] == '\0') { - daemon_log = getenv("ERNOS_LOG_FILE"); - } - if (daemon_log && daemon_log[0] != '\0') { - FILE* f = fopen(daemon_log, "ab"); - if (f) { - time_t rawtime; - time(&rawtime); - struct tm * timeinfo = localtime(&rawtime); - char time_buf[80]; - if (timeinfo) { - strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", timeinfo); - } else { - snprintf(time_buf, sizeof(time_buf), "%lld", (long long)rawtime); - } - fprintf(f, "[%s] FATAL: Runtime Error: %s (signal %d)\n", time_buf, name, sig); - fclose(f); - } - } - - _exit(128 + sig); -} - -#ifdef _MSC_VER -static void ep_install_signal_handlers(void); -#pragma section(".CRT$XCU", read) -__declspec(allocate(".CRT$XCU")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers; -static void ep_install_signal_handlers(void) { -#else -__attribute__((constructor)) -static void ep_install_signal_handlers(void) { -#endif - signal(SIGFPE, ep_signal_handler); - signal(SIGSEGV, ep_signal_handler); - signal(SIGABRT, ep_signal_handler); -#ifdef _WIN32 - { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); } -#endif -} - -#if defined(__wasm__) - typedef int ep_thread_t; - typedef int ep_mutex_t; - typedef int ep_cond_t; - #define ep_mutex_init(m) (void)(0) - #define ep_mutex_lock(m) (void)(0) - #define ep_mutex_unlock(m) (void)(0) - #define ep_cond_init(c) (void)(0) - #define ep_cond_wait(c, m) (void)(0) - #define ep_cond_signal(c) (void)(0) -#elif defined(_WIN32) - #include - #include - #include - #pragma comment(lib, "ws2_32.lib") - typedef HANDLE ep_thread_t; - typedef CRITICAL_SECTION ep_mutex_t; - typedef CONDITION_VARIABLE ep_cond_t; - #define ep_mutex_init(m) InitializeCriticalSection(m) - #define ep_mutex_lock(m) EnterCriticalSection(m) - #define ep_mutex_unlock(m) LeaveCriticalSection(m) - #define ep_cond_init(c) InitializeConditionVariable(c) - #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE) - #define ep_cond_signal(c) WakeConditionVariable(c) -#else - #include - #include - #include - #include - #include - #include - #include - #include - #include - typedef pthread_t ep_thread_t; - typedef pthread_mutex_t ep_mutex_t; - typedef pthread_cond_t ep_cond_t; - #define ep_mutex_init(m) pthread_mutex_init(m, NULL) - #define ep_mutex_lock(m) pthread_mutex_lock(m) - #define ep_mutex_unlock(m) pthread_mutex_unlock(m) - #define ep_cond_init(c) pthread_cond_init(c, NULL) - #define ep_cond_wait(c, m) pthread_cond_wait(c, m) - #define ep_cond_signal(c) pthread_cond_signal(c) -#endif - -/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */ - -#include -#if !defined(__wasm__) && !defined(_WIN32) -#include -#endif - -typedef enum { - EP_OBJ_LIST, - EP_OBJ_STRING, - EP_OBJ_STRUCT, - EP_OBJ_CLOSURE, - EP_OBJ_MAP -} EpObjKind; - -typedef struct EpGCObject { - EpObjKind kind; - int marked; - void* ptr; /* actual allocation pointer */ - long long size; /* payload size for structs */ - long long num_fields; /* number of fields for structs (each is long long) */ - int generation; /* 0 = Nursery/young, 1 = Old */ - struct EpGCObject* next; /* intrusive linked list */ -} EpGCObject; - -long long ep_time_now_ms(void); -long long ep_sleep_ms(long long ms); - -typedef struct EpTask EpTask; -typedef struct { - long long chan; - int completed; - long long value; - EpTask* waiting_task; -} EpFuture; - -static long long ep_await_future(EpFuture* fut); - -struct EpTask { - long long (*step)(void*); /* pointer to step function */ - void* args; /* pointer to step state arguments */ - long long args_size_bytes; /* size of args struct for GC tracing */ - EpTask* next; /* run-queue link pointer */ - EpFuture* fut; /* future associated with this task */ - int state; /* coroutine execution state */ - int is_cancelled; /* cancellation flag for structured concurrency */ - struct EpTask* parent; /* parent task for structured concurrency cancellation */ -}; - -/* Event Loop Scheduler Globals & Functions */ -static EpTask* ep_run_queue_head = NULL; -static EpTask* ep_run_queue_tail = NULL; -static EpTask* ep_current_task = NULL; -static int ep_event_loop_fd = -1; /* epoll or kqueue fd */ -static int ep_active_io_sources = 0; - -static void ep_task_enqueue(EpTask* task) { - if (!task) return; - task->next = NULL; - if (ep_run_queue_tail) { - ep_run_queue_tail->next = task; - ep_run_queue_tail = task; - } else { - ep_run_queue_head = ep_run_queue_tail = task; - } -} - -static EpTask* ep_task_dequeue(void) { - if (!ep_run_queue_head) return NULL; - EpTask* task = ep_run_queue_head; - ep_run_queue_head = ep_run_queue_head->next; - if (!ep_run_queue_head) ep_run_queue_tail = NULL; - return task; -} - -#ifndef __wasm__ -#ifdef __APPLE__ -#include -#else -#include -#endif -#endif - -static void ep_async_loop_init(void) { - if (ep_event_loop_fd != -1) return; -#ifdef __wasm__ - ep_event_loop_fd = 999; -#elif defined(__APPLE__) - ep_event_loop_fd = kqueue(); -#else - ep_event_loop_fd = epoll_create1(0); -#endif -} - -typedef struct EpTimer { - long long expiry_ms; - EpTask* task; - struct EpTimer* next; -} EpTimer; -static EpTimer* ep_timers_head = NULL; - -static void ep_async_register_timer(long long timeout_ms, EpTask* task) { - long long expiry = ep_time_now_ms() + timeout_ms; - EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer)); - timer->expiry_ms = expiry; - timer->task = task; - timer->next = NULL; - - /* Insert sorted */ - if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) { - timer->next = ep_timers_head; - ep_timers_head = timer; - } else { - EpTimer* cur = ep_timers_head; - while (cur->next && cur->next->expiry_ms <= expiry) { - cur = cur->next; - } - timer->next = cur->next; - cur->next = timer; - } -} - -static long long ep_get_next_timer_timeout(void) { - if (!ep_timers_head) return -1; /* block indefinitely */ - long long now = ep_time_now_ms(); - long long diff = ep_timers_head->expiry_ms - now; - return diff < 0 ? 0 : diff; -} - -static void ep_process_expired_timers(void) { - long long now = ep_time_now_ms(); - while (ep_timers_head && ep_timers_head->expiry_ms <= now) { - EpTimer* expired = ep_timers_head; - ep_timers_head = ep_timers_head->next; - ep_task_enqueue(expired->task); - free(expired); - } -} - -static void ep_async_register_read(int fd, EpTask* task) { -#ifdef __wasm__ - (void)fd; - (void)task; -#else - ep_async_loop_init(); - ep_active_io_sources++; -#ifdef __APPLE__ - struct kevent ev; - EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task); - kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL); -#else - struct epoll_event ev; - ev.events = EPOLLIN | EPOLLONESHOT; - ev.data.ptr = task; - if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev); - } -#endif -#endif -} - -static void ep_async_wait_step(long long timeout) { -#ifdef __wasm__ - if (timeout > 0) { - ep_sleep_ms(timeout); - } -#else -#ifdef __APPLE__ - struct kevent events[16]; - struct timespec ts; - struct timespec* p_ts = NULL; - if (timeout >= 0) { - ts.tv_sec = timeout / 1000; - ts.tv_nsec = (timeout % 1000) * 1000000; - p_ts = &ts; - } - int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts); - for (int i = 0; i < n; i++) { - EpTask* t = (EpTask*)events[i].udata; - ep_task_enqueue(t); - ep_active_io_sources--; - } -#else - struct epoll_event events[16]; - int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout); - for (int i = 0; i < n; i++) { - EpTask* t = (EpTask*)events[i].data.ptr; - ep_task_enqueue(t); - ep_active_io_sources--; - } -#endif -#endif - ep_process_expired_timers(); -} - -static void ep_async_loop_run(void) { - ep_async_loop_init(); - while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) { - /* 1. Run all runnable tasks */ - while (ep_run_queue_head) { - EpTask* task = ep_task_dequeue(); - if (task->is_cancelled) { - if (task->fut) { - task->fut->completed = 1; - task->fut->value = -1; - } - free(task->args); - free(task); - continue; - } - ep_current_task = task; - long long res = task->step(task->args); - ep_current_task = NULL; - if (res != -999999) { - if (task->fut) { - task->fut->value = res; - task->fut->completed = 1; - if (task->fut->waiting_task) { - ep_task_enqueue(task->fut->waiting_task); - task->fut->waiting_task = NULL; - } - } - free(task->args); - free(task); - } - } - - /* 2. If no tasks runnable, wait for I/O / timers */ - if (!ep_run_queue_head) { - long long timeout = ep_get_next_timer_timeout(); - if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { - break; - } - - if (ep_event_loop_fd == -1) { - if (timeout > 0) { - ep_sleep_ms(timeout); - } - ep_process_expired_timers(); - continue; - } - - ep_async_wait_step(timeout); - } - } -} - -static long long ep_await_future(EpFuture* fut) { - if (!fut) return 0; - while (!fut->completed) { - if (ep_run_queue_head) { - EpTask* task = ep_task_dequeue(); - if (task) { - if (task->is_cancelled) { - if (task->fut) { - task->fut->completed = 1; - task->fut->value = -1; - } - free(task->args); - free(task); - } else { - EpTask* saved_current = ep_current_task; - ep_current_task = task; - long long res = task->step(task->args); - ep_current_task = saved_current; - if (res != -999999) { - if (task->fut) { - task->fut->value = res; - task->fut->completed = 1; - if (task->fut->waiting_task) { - ep_task_enqueue(task->fut->waiting_task); - task->fut->waiting_task = NULL; - } - } - free(task->args); - free(task); - } - } - } - } else { - long long timeout = ep_get_next_timer_timeout(); - if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { - fprintf(stderr, "Deadlock detected: awaiting incomplete future with no active tasks or timers.\n"); - exit(1); - } - if (ep_event_loop_fd == -1) { - if (timeout > 0) { - ep_sleep_ms(timeout); - } - ep_process_expired_timers(); - } else { - ep_async_wait_step(timeout); - } - } - } - return fut->value; -} - -static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind); -long long create_list(void); -long long append_list(long long list_ptr, long long value); - -typedef struct { - EpFuture* futures[128]; - int count; - int has_error; -} EpTaskGroup; - -typedef struct { - EpFuture* fut; - int timer_fired; -} EpTimeoutArgs; - -static EpTask* ep_find_task_by_future(EpFuture* fut) { - if (!fut) return NULL; - EpTask* cur = ep_run_queue_head; - while (cur) { - if (cur->fut == fut) return cur; - cur = cur->next; - } - EpTimer* timer = ep_timers_head; - while (timer) { - if (timer->task && timer->task->fut == fut) return timer->task; - timer = timer->next; - } - return NULL; -} - -static void ep_cancel_task(EpTask* task) { - if (!task) return; - task->is_cancelled = 1; - if (task->fut) { - task->fut->completed = 1; - task->fut->value = -1; - } - // Cancel children in run queue - EpTask* cur = ep_run_queue_head; - while (cur) { - if (cur->parent == task) { - ep_cancel_task(cur); - } - cur = cur->next; - } - // Cancel children in timers queue - EpTimer* timer = ep_timers_head; - while (timer) { - if (timer->task && timer->task->parent == task) { - ep_cancel_task(timer->task); - } - timer = timer->next; - } -} - -static long long create_task_group(void) { - EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup)); - tg->count = 0; - tg->has_error = 0; - { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; } - return (long long)tg; -} - -static long long add_task_group(long long group_ptr, long long fut_ptr) { - EpTaskGroup* tg = (EpTaskGroup*)group_ptr; - EpFuture* fut = (EpFuture*)fut_ptr; - if (!tg || !fut) return 0; - if (tg->count < 128) { - tg->futures[tg->count++] = fut; - // Associate the task's parent with the current task so it's cancellation-linked - EpTask* task = ep_find_task_by_future(fut); - if (task) { - task->parent = ep_current_task; - } - } - return 0; -} - -static long long wait_task_group(long long group_ptr) { - EpTaskGroup* tg = (EpTaskGroup*)group_ptr; - if (!tg) return 0; - - int all_done = 0; - while (!all_done) { - all_done = 1; - for (int i = 0; i < tg->count; i++) { - EpFuture* fut = tg->futures[i]; - if (!fut->completed) { - all_done = 0; - break; - } - } - - if (all_done) break; - - if (ep_run_queue_head) { - EpTask* task = ep_task_dequeue(); - if (task) { - if (task->is_cancelled) { - if (task->fut) { - task->fut->completed = 1; - task->fut->value = -1; - } - free(task->args); - free(task); - } else { - EpTask* saved_current = ep_current_task; - ep_current_task = task; - long long res = task->step(task->args); - ep_current_task = saved_current; - if (res != -999999) { - if (task->fut) { - task->fut->value = res; - task->fut->completed = 1; - if (task->fut->waiting_task) { - ep_task_enqueue(task->fut->waiting_task); - task->fut->waiting_task = NULL; - } - } - free(task->args); - free(task); - } - } - } - } else { - long long timeout = ep_get_next_timer_timeout(); - if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { - fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n"); - exit(1); - } - if (ep_event_loop_fd == -1) { - if (timeout > 0) { - ep_sleep_ms(timeout); - } - ep_process_expired_timers(); - } else { - ep_async_wait_step(timeout); - } - } - - // Propagate cancellation/failure inside task group - for (int i = 0; i < tg->count; i++) { - EpFuture* fut = tg->futures[i]; - if (fut->completed && fut->value == -1) { - tg->has_error = 1; - for (int j = 0; j < tg->count; j++) { - EpFuture* other_fut = tg->futures[j]; - if (!other_fut->completed) { - EpTask* other_task = ep_find_task_by_future(other_fut); - if (other_task) { - ep_cancel_task(other_task); - } else { - other_fut->completed = 1; - other_fut->value = -1; - } - } - } - } - } - } - - long long list = create_list(); - for (int i = 0; i < tg->count; i++) { - append_list(list, tg->futures[i]->value); - } - return list; -} - -static long long ep_timeout_timer_step(void* r) { - EpTimeoutArgs* args = (EpTimeoutArgs*)r; - if (args && args->fut && !args->fut->completed) { - args->timer_fired = 1; - EpTask* task = ep_find_task_by_future(args->fut); - if (task) { - ep_cancel_task(task); - } else { - args->fut->completed = 1; - args->fut->value = -1; - } - } - return 0; -} - -static long long async_timeout(long long timeout_ms, long long fut_ptr) { - EpFuture* fut = (EpFuture*)fut_ptr; - if (!fut) return -1; - if (fut->completed) return fut->value; - - EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs)); - args->fut = fut; - args->timer_fired = 0; - - EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask)); - timer_task->step = ep_timeout_timer_step; - timer_task->args = args; - timer_task->args_size_bytes = sizeof(EpTimeoutArgs); - timer_task->fut = NULL; - timer_task->state = 0; - timer_task->is_cancelled = 0; - timer_task->parent = NULL; - - ep_async_register_timer(timeout_ms, timer_task); - - while (!fut->completed && !(args->timer_fired)) { - if (ep_run_queue_head) { - EpTask* task = ep_task_dequeue(); - if (task) { - if (task->is_cancelled) { - if (task->fut) { - task->fut->completed = 1; - task->fut->value = -1; - } - free(task->args); - free(task); - } else { - EpTask* saved_current = ep_current_task; - ep_current_task = task; - long long res = task->step(task->args); - ep_current_task = saved_current; - if (res != -999999) { - if (task->fut) { - task->fut->value = res; - task->fut->completed = 1; - if (task->fut->waiting_task) { - ep_task_enqueue(task->fut->waiting_task); - task->fut->waiting_task = NULL; - } - } - free(task->args); - free(task); - } - } - } - } else { - long long timeout = ep_get_next_timer_timeout(); - if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { - break; - } - if (ep_event_loop_fd == -1) { - if (timeout > 0) { - ep_sleep_ms(timeout); - } - ep_process_expired_timers(); - } else { - ep_async_wait_step(timeout); - } - } - } - - return fut->value; -} - -/* ── Awaitable async socket-readability ───────────────────────────────────── - `await async_wait_readable(fd)` suspends the calling async task until `fd` is - readable, letting the event loop run other tasks (e.g. another agent waiting on - its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a - oneshot read-readiness task with the loop, return the future. When fd becomes - readable, ep_async_wait_step re-enqueues the task; its step completes the future - and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently - on ONE thread — no OS threads, no shared-heap GC race. */ -typedef struct { EpFuture* fut; } EpReadReadyArgs; -static long long ep_read_ready_step(void* r) { - EpReadReadyArgs* args = (EpReadReadyArgs*)r; - if (args && args->fut) { - args->fut->completed = 1; - args->fut->value = 1; - if (args->fut->waiting_task) { - ep_task_enqueue(args->fut->waiting_task); - args->fut->waiting_task = NULL; - } - } - return 0; -} -long long async_wait_readable(long long fd) { - EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture)); - fut->completed = 0; - fut->value = 0; - fut->waiting_task = NULL; - fut->chan = 0; - { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; } - EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs)); - args->fut = fut; - EpTask* task = (EpTask*)malloc(sizeof(EpTask)); - task->step = ep_read_ready_step; - task->args = args; - task->args_size_bytes = sizeof(EpReadReadyArgs); - task->fut = NULL; - task->state = 0; - task->is_cancelled = 0; - task->parent = ep_current_task; - ep_async_register_read((int)fd, task); - return (long long)fut; -} - -typedef struct { - EpFuture* fut; -} EpSleepTimerArgs; - -static long long ep_sleep_timer_step(void* r) { - EpSleepTimerArgs* args = (EpSleepTimerArgs*)r; - if (args && args->fut) { - args->fut->completed = 1; - args->fut->value = 0; - if (args->fut->waiting_task) { - ep_task_enqueue(args->fut->waiting_task); - args->fut->waiting_task = NULL; - } - } - return 0; -} - -static long long sleep_ms(long long ms) { - EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture)); - fut->completed = 0; - fut->value = 0; - fut->waiting_task = NULL; - fut->chan = 0; - { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; } - - EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs)); - args->fut = fut; - - EpTask* task = (EpTask*)malloc(sizeof(EpTask)); - task->step = ep_sleep_timer_step; - task->args = args; - task->args_size_bytes = sizeof(EpSleepTimerArgs); - task->fut = NULL; - task->state = 0; - task->is_cancelled = 0; - task->parent = ep_current_task; - - ep_async_register_timer(ms, task); - return (long long)fut; -} - -static long long cancel_task(long long fut_ptr) { - EpFuture* fut = (EpFuture*)fut_ptr; - if (fut) { - EpTask* task = ep_find_task_by_future(fut); - if (task) { - ep_cancel_task(task); - } else { - fut->completed = 1; - fut->value = -1; - } - } - return 0; -} - -/* Closure environment — captures travel with the function pointer */ -#define EP_CLOSURE_MAGIC 0x4550434C4FL -typedef struct { - long long magic; - long long fn_ptr; - long long env[]; /* flexible array of captured values */ -} EpClosure; - -/* GC globals */ -static EpGCObject* ep_gc_head = NULL; -static long long ep_gc_count = 0; -static long long ep_gc_threshold = 4096; -static int ep_gc_enabled = 1; -static long long ep_gc_nursery_count = 0; -static long long ep_gc_nursery_threshold = 512; -static int ep_gc_minor_count = 0; -static int ep_gc_major_count = 0; -static void** ep_gc_remembered_set = NULL; -static long long ep_gc_remembered_cap = 0; -static long long ep_gc_remembered_size = 0; -/* Single mutex for ALL GC and thread registry operations. - Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex) - which caused deadlock under concurrent channel load: thread A held gc_mutex - and waited for registry_mutex, thread B held registry_mutex and waited for - gc_mutex. Single lock eliminates the ordering problem. */ -#ifdef __wasm__ -#define __thread -#endif -static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in - ep_gc_stop_the_world(), waits until every *other* registered thread has parked - at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs - concurrently with a mutator changing its roots or an object's fields — the - "marking races with running mutators" hazard. All three fields are touched - only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at - safepoints are a benign optimization: a missed set just defers parking to the - next safepoint, and the collector's bounded wait covers it). */ -static volatile int ep_gc_stop_requested = 0; -static int ep_gc_parked_count = 0; -static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER; - -/* Function pointer for channel scanning — set after EpChannel is defined. - GC mark calls this to scan values in-transit in channel buffers. */ -static void (*ep_gc_scan_channels_major)(void) = NULL; -static void (*ep_gc_scan_channels_minor)(void) = NULL; -/* Function pointers for marking top-level constant/global variables, which are - GC roots that live outside any function frame. Set by __ep_init_constants. */ -static void (*ep_gc_mark_globals_major)(void) = NULL; -static void (*ep_gc_mark_globals_minor)(void) = NULL; -/* Function pointers for map value traversal — set after EpMap is defined. - GC mark calls these to recursively mark values stored in maps. */ -static void (*ep_gc_mark_map_values)(void* ptr) = NULL; -static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL; - -/* Thread registry for GC root scanning in multi-threaded environment */ -#define EP_MAX_THREADS 256 -static __thread void* volatile ep_thread_local_top = NULL; -static __thread void* ep_thread_local_bottom = NULL; - -static void* volatile* ep_thread_tops[EP_MAX_THREADS]; -static void* ep_thread_bottoms[EP_MAX_THREADS]; -static volatile int ep_thread_active[EP_MAX_THREADS]; -static int ep_num_threads = 0; - -/* Per-thread GC root state — heap-allocated, stable across thread lifetime. - Previous design stored raw pointers to __thread arrays (ep_gc_root_stack, - ep_gc_root_sp) in the global registry. When a thread exited, the __thread - storage was freed, leaving dangling pointers that ep_gc_mark would - dereference → segfault. Now each thread gets a heap-allocated state struct - that survives thread exit and is only recycled when the slot is reused. */ -typedef struct { - long long* roots[4096]; /* copy of root pointers, updated under lock */ - volatile int sp; /* current root stack pointer */ -} EpThreadGCState; - -static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS]; - -/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */ -#define EP_GC_MAX_ROOTS 4096 -static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS]; -static __thread int ep_gc_root_sp = 0; -static __thread int ep_thread_slot = -1; - -/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on - push and retreats on pop so that per-frame push/pop counts stay balanced. - Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further - roots are counted but not stored (those deep-overflow locals are simply not - traced) — crucially, we never overwrite or drop an outer frame's stored - roots, which the old "silently skip the push but still pop" path did. */ -static void ep_gc_push_root(long long* root) { - int idx = ep_gc_root_sp; - ep_gc_root_sp++; - if (idx < EP_GC_MAX_ROOTS) { - ep_gc_root_stack[idx] = root; - /* Update the heap-allocated state so GC mark can see it safely */ - if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) { - ep_thread_gc_states[ep_thread_slot]->roots[idx] = root; - ep_thread_gc_states[ep_thread_slot]->sp = - (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS; - } - } -} -static void ep_gc_pop_roots(long long count) { - ep_gc_root_sp -= (int)count; - if (ep_gc_root_sp < 0) ep_gc_root_sp = 0; - /* Update the heap-allocated state (clamped to the array bound) */ - if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) { - ep_thread_gc_states[ep_thread_slot]->sp = - (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS; - } -} - -/* Park the calling thread if the collector has stopped the world. - MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise - root set) is stable while parked, so the collector can scan it race-free. */ -static void ep_gc_park_if_stopped(void) { - if (!ep_gc_stop_requested) return; - /* Spill registers onto the stack and publish this thread's current stack top - so the collector can conservatively scan its frozen C stack while parked — - this catches roots held only in registers/temporaries that the precise - shadow stack does not yet record. _dummy is declared below _pregs, so its - (lower) address bounds a scan range that covers the spilled registers. */ - jmp_buf _pregs; - volatile char _top_marker; /* function-scope: stays valid while parked */ - memset(&_pregs, 0, sizeof(_pregs)); - setjmp(_pregs); - /* _top_marker is declared after _pregs, so its (lower) address bounds a scan - range [&_top_marker, stack_bottom] that covers the spilled registers. */ - ep_thread_local_top = (void*)&_top_marker; - __sync_synchronize(); /* publish shadow-stack + top writes before parking */ - ep_gc_parked_count++; - while (ep_gc_stop_requested) { - pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex); - } - ep_gc_parked_count--; -} - -/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held. - Waits (briefly releasing the lock so blocked mutators can reach a safepoint) - until all other registered threads have parked. After a bounded fallback - (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked - or idle with a stable shadow stack, so scanning it is still safe in practice. */ -static void ep_gc_stop_the_world(void) { - ep_gc_stop_requested = 1; - /* Actively-running threads reach a safepoint (every allocation and every - function entry) within microseconds, so they park on the first spin or - two. The bound only caps the rare case where a thread is blocked/idle - (e.g. just entered a channel op) and won't park — those have a stable - shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */ - for (int spins = 0; spins < 40; spins++) { - int others = 0; - for (int t = 0; t < ep_num_threads; t++) { - if (ep_thread_active[t] && t != ep_thread_slot) others++; - } - if (others <= 0 || ep_gc_parked_count >= others) return; - pthread_mutex_unlock(&ep_gc_mutex); -#ifdef _WIN32 - Sleep(1); -#elif !defined(__wasm__) - usleep(250); -#endif - pthread_mutex_lock(&ep_gc_mutex); - } -} - -/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */ -static void ep_gc_start_the_world(void) { - ep_gc_stop_requested = 0; - pthread_cond_broadcast(&ep_gc_resume_cond); -} - -static void ep_gc_register_thread(void* stack_bottom) { - ep_thread_local_bottom = stack_bottom; - ep_thread_local_top = stack_bottom; - - pthread_mutex_lock(&ep_gc_mutex); - int slot = -1; - for (int i = 0; i < ep_num_threads; i++) { - if (!ep_thread_active[i]) { - slot = i; - break; - } - } - if (slot == -1 && ep_num_threads < EP_MAX_THREADS) { - slot = ep_num_threads++; - } - if (slot != -1) { - ep_thread_tops[slot] = &ep_thread_local_top; - ep_thread_bottoms[slot] = stack_bottom; - /* Allocate or reuse heap state for this slot */ - if (!ep_thread_gc_states[slot]) { - ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState)); - } - ep_thread_gc_states[slot]->sp = 0; - ep_thread_slot = slot; - __sync_synchronize(); /* Memory barrier: state must be visible before active */ - ep_thread_active[slot] = 1; - } - pthread_mutex_unlock(&ep_gc_mutex); -} - -static void ep_gc_unregister_thread(void) { - pthread_mutex_lock(&ep_gc_mutex); - for (int i = 0; i < ep_num_threads; i++) { - if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) { - /* Zero root count FIRST — even if ep_gc_mark races past the - active check, it will see sp=0 and walk no roots instead - of dereferencing stale __thread pointers */ - if (ep_thread_gc_states[i]) { - ep_thread_gc_states[i]->sp = 0; - } - __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */ - ep_thread_active[i] = 0; - ep_thread_slot = -1; - break; - } - } - pthread_mutex_unlock(&ep_gc_mutex); -} - -#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; } - -/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */ -typedef struct { - void* key; - EpGCObject* value; -} EpGCEntry; - -static EpGCEntry* ep_gc_table = NULL; -static long long ep_gc_table_cap = 0; -static long long ep_gc_table_size = 0; - -/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap) - with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4 - bits are always 0 and only every 16th bucket was ever a home slot. That caused - catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's - rehash became O(n^2), which (under the single global GC mutex) wedged the whole - node when a large object list was freed. A splitmix64 finalizer avalanches all - bits, so even the low bits taken by the (cap-1) mask are well distributed. */ -static inline long long ep_gc_index(void* key, long long cap) { - uint64_t z = (uint64_t)(uintptr_t)key; - z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; - z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; - z = z ^ (z >> 31); - return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */ -} - -/* Insert without growing — assumes a free slot exists. Used by the resize and by - ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of - the table mid-iteration. */ -static void ep_gc_table_place(void* key, EpGCObject* value) { - long long idx = ep_gc_index(key, ep_gc_table_cap); - while (ep_gc_table[idx].key != NULL) { - if (ep_gc_table[idx].key == key) { - ep_gc_table[idx].value = value; - return; - } - idx = (idx + 1) & (ep_gc_table_cap - 1); - } - ep_gc_table[idx].key = key; - ep_gc_table[idx].value = value; - ep_gc_table_size++; -} - -static void ep_gc_table_insert(void* key, EpGCObject* value) { - if (ep_gc_table_size * 2 >= ep_gc_table_cap) { - long long old_cap = ep_gc_table_cap; - long long new_cap = old_cap == 0 ? 512 : old_cap * 2; - EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry)); - EpGCEntry* old_table = ep_gc_table; - ep_gc_table = new_table; - ep_gc_table_cap = new_cap; - ep_gc_table_size = 0; - for (long long i = 0; i < old_cap; i++) { - if (old_table[i].key != NULL) { - ep_gc_table_place(old_table[i].key, old_table[i].value); - } - } - free(old_table); - } - ep_gc_table_place(key, value); -} - -static EpGCObject* ep_gc_table_get(void* key) { - if (ep_gc_table_cap == 0) return NULL; - long long idx = ep_gc_index(key, ep_gc_table_cap); - while (ep_gc_table[idx].key != NULL) { - if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value; - idx = (idx + 1) & (ep_gc_table_cap - 1); - } - return NULL; -} - -static void ep_gc_table_remove(void* key) { - if (ep_gc_table_cap == 0) return; - long long idx = ep_gc_index(key, ep_gc_table_cap); - while (ep_gc_table[idx].key != NULL) { - if (ep_gc_table[idx].key == key) { - ep_gc_table[idx].key = NULL; - ep_gc_table[idx].value = NULL; - ep_gc_table_size--; - /* Backward-shift rehash of the rest of this cluster. Re-place (no - resize: size is not growing) so a mid-iteration realloc can never - free the table out from under this loop. */ - long long next_idx = (idx + 1) & (ep_gc_table_cap - 1); - while (ep_gc_table[next_idx].key != NULL) { - void* rehash_key = ep_gc_table[next_idx].key; - EpGCObject* rehash_val = ep_gc_table[next_idx].value; - ep_gc_table[next_idx].key = NULL; - ep_gc_table[next_idx].value = NULL; - ep_gc_table_size--; - ep_gc_table_place(rehash_key, rehash_val); - next_idx = (next_idx + 1) & (ep_gc_table_cap - 1); - } - return; - } - idx = (idx + 1) & (ep_gc_table_cap - 1); - } -} - - - -/* Register a new GC object */ -static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) { - if (!ptr) return NULL; - pthread_mutex_lock(&ep_gc_mutex); - ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */ - EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject)); - if (!obj) { - pthread_mutex_unlock(&ep_gc_mutex); - return NULL; - } - obj->kind = kind; - obj->marked = 0; - obj->ptr = ptr; - obj->size = 0; - obj->num_fields = 0; - obj->generation = 0; - obj->next = ep_gc_head; - ep_gc_head = obj; - ep_gc_count++; - ep_gc_nursery_count++; - ep_gc_table_insert(ptr, obj); - pthread_mutex_unlock(&ep_gc_mutex); - return obj; -} - -/* Find GC object by pointer. - Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table - concurrently (from another thread's allocation). Mutator-side callers - (write barrier, free_struct/free_map/free_list, to-string) must use this - locking variant; code already holding the mutex (mark/sweep) calls - ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */ -static EpGCObject* ep_gc_find(void* ptr) { - pthread_mutex_lock(&ep_gc_mutex); - ep_gc_park_if_stopped(); /* safepoint */ - EpGCObject* obj = ep_gc_table_get(ptr); - pthread_mutex_unlock(&ep_gc_mutex); - return obj; -} - -/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0). - The whole operation runs under ep_gc_mutex so the table lookups and the - remembered-set update see a consistent table (no race with a concurrent - resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */ -static void ep_gc_write_barrier(void* host_ptr, long long val) { - if (val == 0) return; - pthread_mutex_lock(&ep_gc_mutex); - ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */ - EpGCObject* host_obj = ep_gc_table_get(host_ptr); - EpGCObject* val_obj = ep_gc_table_get((void*)val); - if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) { - /* Check if already in remembered set */ - int found = 0; - for (long long i = 0; i < ep_gc_remembered_size; i++) { - if (ep_gc_remembered_set[i] == (void*)val) { - found = 1; - break; - } - } - if (!found) { - if (ep_gc_remembered_size >= ep_gc_remembered_cap) { - long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2; - void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*)); - if (new_set) { - ep_gc_remembered_set = new_set; - ep_gc_remembered_cap = new_cap; - } - } - if (ep_gc_remembered_size < ep_gc_remembered_cap) { - ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val; - } - } - } - pthread_mutex_unlock(&ep_gc_mutex); -} - -/* Forward declarations for list type (needed by GC mark) */ -typedef struct { - long long* data; - long long length; - long long capacity; -} EpList; - -/* A real heap object (list/map/string) is malloc'd, so its address is far above - the never-mapped first page. EP values that are NOT pointers — small ints, - booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096). - Guarding the object accessors with this turns "deref a non-pointer" (the cause - of the read_transcripts segfault, and that whole class) into a safe null return - instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */ -#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL) - -/* Mark a single object and recursively mark its children */ -static void ep_gc_mark_object(void* ptr) { - if (!ptr) return; - /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */ - EpGCObject* obj = ep_gc_table_get(ptr); - if (!obj || obj->marked) return; - obj->marked = 1; - - if (obj->kind == EP_OBJ_LIST) { - EpList* list = (EpList*)ptr; - for (long long i = 0; i < list->length; i++) { - long long val = list->data[i]; - if (val != 0) { - ep_gc_mark_object((void*)val); - } - } - } else if (obj->kind == EP_OBJ_STRUCT) { - long long* fields = (long long*)ptr; - for (long long i = 0; i < obj->num_fields; i++) { - if (fields[i] != 0) { - ep_gc_mark_object((void*)fields[i]); - } - } - } else if (obj->kind == EP_OBJ_MAP) { - if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr); - } -} - -/* Mark a single object and recursively mark its children (only if it is Gen 0) */ -static void ep_gc_mark_object_minor(void* ptr) { - if (!ptr) return; - /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */ - EpGCObject* obj = ep_gc_table_get(ptr); - if (!obj || obj->generation != 0 || obj->marked) return; - obj->marked = 1; - - if (obj->kind == EP_OBJ_LIST) { - EpList* list = (EpList*)ptr; - for (long long i = 0; i < list->length; i++) { - long long val = list->data[i]; - if (val != 0) { - ep_gc_mark_object_minor((void*)val); - } - } - } else if (obj->kind == EP_OBJ_STRUCT) { - long long* fields = (long long*)ptr; - for (long long i = 0; i < obj->num_fields; i++) { - if (fields[i] != 0) { - ep_gc_mark_object_minor((void*)fields[i]); - } - } - } else if (obj->kind == EP_OBJ_MAP) { - if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr); - } -} - -/* Conservatively scan every registered thread's C stack and mark any word that - looks like a tracked pointer. The collector spills its own registers and - publishes its top here; all other threads are parked at a safepoint with their - registers spilled and top published (ep_gc_park_if_stopped), so their stacks - are frozen. This complements the precise shadow stacks: it catches roots held - only in registers/temporaries (e.g. a freshly allocated object not yet stored - into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find. - - Only run on MAJOR collections: minor collections rely on the precise shadow - stacks plus the write barrier's remembered set (the standard generational - approach), so they do no stack scan at all — which means there is no racy - cross-thread stack read on the frequent minor path either. The expensive - full-stack scan is paid only on the rarer major collection, where it pins - any long-lived object reachable only via a register across many GCs. - - Marked no_sanitize_address: a conservative scan deliberately reads whole stack - ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */ -#if defined(__SANITIZE_ADDRESS__) -# define EP_NO_ASAN __attribute__((no_sanitize_address)) -#elif defined(__has_feature) -# if __has_feature(address_sanitizer) -# define EP_NO_ASAN __attribute__((no_sanitize_address)) -# endif -#endif -#ifndef EP_NO_ASAN -# define EP_NO_ASAN -#endif -EP_NO_ASAN -static void ep_gc_scan_thread_stacks(void) { - jmp_buf _regs; - volatile char _top_marker; - memset(&_regs, 0, sizeof(_regs)); - setjmp(_regs); /* spill the collector's own registers onto its stack */ - /* Publish the LOWEST of our own local addresses as this thread's live top, so the - scanned range covers both the stack marker and the register-spill buffer whatever - order the compiler laid them out (a missed _regs would drop a register-only root). */ - { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs; - ep_thread_local_top = (void*)((_a < _b) ? _a : _b); } - for (int t = 0; t < ep_num_threads; t++) { - if (!ep_thread_active[t]) continue; - if (!ep_thread_tops[t]) continue; - void** start = (void**)*ep_thread_tops[t]; - void** end = (void**)ep_thread_bottoms[t]; - if (!start || !end) continue; - if (start > end) { void** tmp = start; start = end; end = tmp; } - for (void** cur = start; cur < end; cur++) { - void* p = *cur; - if (p) ep_gc_mark_object(p); - } - } -} - -/* Mark phase: traverse from ALL threads' explicit GC roots. - Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */ -static void ep_gc_mark(void) { - ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */ - for (int t = 0; t < ep_num_threads; t++) { - if (!ep_thread_active[t]) continue; - EpThreadGCState* state = ep_thread_gc_states[t]; - if (!state) continue; - int sp = state->sp; - if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue; - for (int i = 0; i < sp; i++) { - long long* root_ptr = state->roots[i]; - if (!root_ptr) continue; - long long val = *root_ptr; - if (val != 0) { - ep_gc_mark_object((void*)val); - } - } - } - /* Also mark from main thread's local root stack (thread 0 / unregistered) */ - int local_sp = ep_gc_root_sp; - if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS; - for (int i = 0; i < local_sp; i++) { - long long val = *ep_gc_root_stack[i]; - if (val != 0) { - ep_gc_mark_object((void*)val); - } - } - /* Mark active tasks in the scheduler run queue */ - EpTask* task = ep_run_queue_head; - while (task) { - if (task->fut) { - ep_gc_mark_object((void*)task->fut); - } - if (task->args && task->args_size_bytes > 0) { - long long* ptr = (long long*)task->args; - for (int i = 0; i < task->args_size_bytes / 8; i++) { - long long val = ptr[i]; - if (val != 0) ep_gc_mark_object((void*)val); - } - } - task = task->next; - } - /* Mark active tasks in the timers queue */ - EpTimer* timer = ep_timers_head; - while (timer) { - if (timer->task) { - EpTask* t = timer->task; - if (t->fut) { - ep_gc_mark_object((void*)t->fut); - } - if (t->args && t->args_size_bytes > 0) { - long long* ptr = (long long*)t->args; - for (int i = 0; i < t->args_size_bytes / 8; i++) { - long long val = ptr[i]; - if (val != 0) ep_gc_mark_object((void*)val); - } - } - } - timer = timer->next; - } - /* Mark top-level constant/global variables (roots outside any frame) */ - if (ep_gc_mark_globals_major) ep_gc_mark_globals_major(); - /* Scan all registered channel buffers — values in-transit have no root */ - if (ep_gc_scan_channels_major) ep_gc_scan_channels_major(); -} - -/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it - finds. This closes a use-after-free on the frequent minor path: a freshly-allocated - argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating - h() — lives only on the C stack / in registers and is not yet on the precise shadow - stack, so a minor collection triggered mid-expression would otherwise free it. Scanning - ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap - (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by - ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held - roots onto the stack so the scan can see them. */ -EP_NO_ASAN -static void ep_gc_scan_own_stack_minor(void) { - jmp_buf _regs; - volatile char _marker; - memset(&_regs, 0, sizeof(_regs)); - setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */ - void* bottom = ep_thread_local_bottom; - if (!bottom) return; - /* Start at the LOWEST of our own local addresses so the scanned range covers both - the current stack top (_marker) and the register-spill buffer (_regs), regardless - of how the compiler ordered these locals on the stack. Missing _regs would drop a - root held only in a callee-saved register -> a rare use-after-free. */ - char* a = (char*)(void*)&_marker; - char* b = (char*)(void*)&_regs; - char* lo = (a < b) ? a : b; - void** start = (void**)lo; - void** end = (void**)bottom; - if (start > end) { void** tmp = start; start = end; end = tmp; } - for (void** cur = start; cur < end; cur++) { - void* p = *cur; - if (p) ep_gc_mark_object_minor(p); - } -} - -static void ep_gc_mark_minor(void) { - /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument - temporaries (only on the stack / in registers, not yet on the shadow stack) that a - minor collection mid-expression would otherwise free. Own-thread only, so race-free. */ - ep_gc_scan_own_stack_minor(); - for (int t = 0; t < ep_num_threads; t++) { - if (!ep_thread_active[t]) continue; - EpThreadGCState* state = ep_thread_gc_states[t]; - if (!state) continue; - int sp = state->sp; - if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue; - for (int i = 0; i < sp; i++) { - long long* root_ptr = state->roots[i]; - if (!root_ptr) continue; - long long val = *root_ptr; - if (val != 0) { - ep_gc_mark_object_minor((void*)val); - } - } - } - int local_sp = ep_gc_root_sp; - if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS; - for (int i = 0; i < local_sp; i++) { - long long val = *ep_gc_root_stack[i]; - if (val != 0) { - ep_gc_mark_object_minor((void*)val); - } - } - /* Mark active tasks in the scheduler run queue for minor collection */ - EpTask* task = ep_run_queue_head; - while (task) { - if (task->fut) { - ep_gc_mark_object_minor((void*)task->fut); - } - if (task->args && task->args_size_bytes > 0) { - long long* ptr = (long long*)task->args; - for (int i = 0; i < task->args_size_bytes / 8; i++) { - long long val = ptr[i]; - if (val != 0) ep_gc_mark_object_minor((void*)val); - } - } - task = task->next; - } - /* Mark active tasks in the timers queue for minor collection */ - EpTimer* timer = ep_timers_head; - while (timer) { - if (timer->task) { - EpTask* t = timer->task; - if (t->fut) { - ep_gc_mark_object_minor((void*)t->fut); - } - if (t->args && t->args_size_bytes > 0) { - long long* ptr = (long long*)t->args; - for (int i = 0; i < t->args_size_bytes / 8; i++) { - long long val = ptr[i]; - if (val != 0) ep_gc_mark_object_minor((void*)val); - } - } - } - timer = timer->next; - } - /* Also mark from the remembered set */ - for (long long i = 0; i < ep_gc_remembered_size; i++) { - ep_gc_mark_object_minor(ep_gc_remembered_set[i]); - } - /* Mark top-level constant/global variables (roots outside any frame) */ - if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor(); - /* Scan all registered channel buffers — values in-transit have no root */ - if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor(); -} - -static void ep_gc_sweep_minor(void) { - EpGCObject** cur = &ep_gc_head; - while (*cur) { - if ((*cur)->generation == 0) { - if (!(*cur)->marked) { - EpGCObject* garbage = *cur; - *cur = garbage->next; - ep_gc_table_remove(garbage->ptr); - if (garbage->kind == EP_OBJ_LIST) { - EpList* list = (EpList*)garbage->ptr; - if (list) { - free(list->data); - free(list); - } - } else if (garbage->kind == EP_OBJ_STRING) { - free(garbage->ptr); - } else if (garbage->kind == EP_OBJ_STRUCT) { - free(garbage->ptr); - } else if (garbage->kind == EP_OBJ_CLOSURE) { - free(garbage->ptr); - } else if (garbage->kind == EP_OBJ_MAP) { - /* EpMap layout: entries*, capacity, size. Free entries then map. */ - void** map_fields = (void**)garbage->ptr; - if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */ - free(garbage->ptr); - } - free(garbage); - ep_gc_count--; - ep_gc_nursery_count--; - } else { - (*cur)->marked = 0; - (*cur)->generation = 1; - ep_gc_nursery_count--; - cur = &(*cur)->next; - } - } else { - cur = &(*cur)->next; - } - } - ep_gc_remembered_size = 0; -} - -static void ep_gc_sweep_major(void) { - EpGCObject** cur = &ep_gc_head; - while (*cur) { - if (!(*cur)->marked) { - EpGCObject* garbage = *cur; - *cur = garbage->next; - ep_gc_table_remove(garbage->ptr); - if (garbage->generation == 0) { - ep_gc_nursery_count--; - } - if (garbage->kind == EP_OBJ_LIST) { - EpList* list = (EpList*)garbage->ptr; - if (list) { - free(list->data); - free(list); - } - } else if (garbage->kind == EP_OBJ_STRING) { - free(garbage->ptr); - } else if (garbage->kind == EP_OBJ_STRUCT) { - free(garbage->ptr); - } else if (garbage->kind == EP_OBJ_CLOSURE) { - free(garbage->ptr); - } else if (garbage->kind == EP_OBJ_MAP) { - void** map_fields = (void**)garbage->ptr; - if (map_fields && map_fields[0]) free(map_fields[0]); - free(garbage->ptr); - } - free(garbage); - ep_gc_count--; - } else { - (*cur)->marked = 0; - if ((*cur)->generation == 0) { - (*cur)->generation = 1; - ep_gc_nursery_count--; - } - cur = &(*cur)->next; - } - } - ep_gc_remembered_size = 0; -} - -static void ep_gc_collect_minor(void) { - if (!ep_gc_enabled) return; - ep_gc_minor_count++; - ep_gc_mark_minor(); - ep_gc_sweep_minor(); -} - -static void ep_gc_collect_major(void) { - if (!ep_gc_enabled) return; - ep_gc_major_count++; - ep_gc_mark(); - ep_gc_sweep_major(); - ep_gc_threshold = ep_gc_count * 2; - if (ep_gc_threshold < 4096) ep_gc_threshold = 4096; -} - -/* Run a full GC collection — caller MUST hold ep_gc_mutex */ -static void ep_gc_collect(void) { - ep_gc_collect_major(); -} - -/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function - GC safepoint: if another thread has stopped the world, park here until it's done. */ -static void ep_gc_maybe_collect(void) { - if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */ - /* Safepoint: lock-free fast check, then park under the lock if a collection - is in progress on another thread. Keeps the no-GC path lock-free. */ - if (ep_gc_stop_requested) { - pthread_mutex_lock(&ep_gc_mutex); - ep_gc_park_if_stopped(); - pthread_mutex_unlock(&ep_gc_mutex); - } - /* Fast path: check thresholds before acquiring mutex. - Counters are only incremented under the mutex, so worst case - we miss one collection cycle — safe trade-off for avoiding - a mutex lock/unlock (~20-50ns) on every function call. */ - if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return; - EP_GC_UPDATE_TOP(); - pthread_mutex_lock(&ep_gc_mutex); - /* Another thread may have started collecting between the check and the lock — - park instead of racing it, then re-check thresholds under the lock. */ - ep_gc_park_if_stopped(); - if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) { - ep_gc_stop_the_world(); - if (ep_gc_nursery_count >= ep_gc_nursery_threshold) { - ep_gc_collect_minor(); - } - if (ep_gc_count >= ep_gc_threshold) { - ep_gc_collect_major(); - } - ep_gc_start_the_world(); - } - pthread_mutex_unlock(&ep_gc_mutex); -} - -/* Unregister an object (for explicit free — removes from GC tracking) */ -static void ep_gc_unregister(void* ptr) { - if (!ptr) return; - pthread_mutex_lock(&ep_gc_mutex); - ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */ - /* Clean up references from the remembered set to prevent dangling pointers */ - for (long long i = 0; i < ep_gc_remembered_size; ) { - if (ep_gc_remembered_set[i] == ptr) { - for (long long j = i; j < ep_gc_remembered_size - 1; j++) { - ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1]; - } - ep_gc_remembered_size--; - } else { - i++; - } - } - ep_gc_table_remove(ptr); - EpGCObject** cur = &ep_gc_head; - while (*cur) { - if ((*cur)->ptr == ptr) { - EpGCObject* found = *cur; - *cur = found->next; - if (found->generation == 0) { - ep_gc_nursery_count--; - } - free(found); - ep_gc_count--; - pthread_mutex_unlock(&ep_gc_mutex); - return; - } - cur = &(*cur)->next; - } - pthread_mutex_unlock(&ep_gc_mutex); -} - -/* Cleanup all remaining GC objects (called at program exit) */ -static void ep_gc_shutdown(void) { - ep_gc_enabled = 0; - /* Only free GC bookkeeping structures, not the tracked objects themselves. - The RAII auto-cleanup has already freed owned objects, and the OS will - reclaim everything else on process exit. Attempting to free individual - objects here causes double-free aborts when RAII and GC both track - the same allocation. */ - EpGCObject* cur = ep_gc_head; - while (cur) { - EpGCObject* next = cur->next; - free(cur); /* free the GCObject wrapper only */ - cur = next; - } - ep_gc_head = NULL; - ep_gc_count = 0; - if (ep_gc_table) { - free(ep_gc_table); - ep_gc_table = NULL; - } - ep_gc_table_cap = 0; - ep_gc_table_size = 0; -} - -/* ========== End Garbage Collector ========== */ - -long long create_list(void); -long long append_list(long long list_ptr, long long value); -long long get_list(long long list_ptr, long long index); -long long set_list(long long list_ptr, long long index, long long value); -long long length_list(long long list_ptr); -long long free_list(long long list_ptr); -long long pop_list(long long list_ptr); -long long remove_list(long long list_ptr, long long index); -char* string_from_list(long long list_ptr); -long long string_to_list(const char* s); -long long string_length(const char* s); -long long display_string(const char* s); -long long file_read(long long path_val); -long long file_write(long long path_val, long long content_val); -long long file_append(long long path_val, long long content_val); -long long file_exists(long long path_val); -long long string_contains(long long s_val, long long sub_val); -long long string_index_of(long long s_val, long long sub_val); -long long string_replace(long long s_val, long long old_val, long long new_val); -long long string_upper(long long s_val); -long long string_lower(long long s_val); -long long string_trim(long long s_val); -long long string_split(long long s_val, long long delim_val); -long long char_at(long long s_val, long long index); -long long char_from_code(long long code); -long long ep_abs(long long n); -long long json_get_string(long long json_val, long long key_val); -long long json_get_int(long long json_val, long long key_val); -long long json_get_bool(long long json_val, long long key_val); -long long ep_sha1(long long data_val); -long long ep_net_recv_bytes(long long fd, long long count); -long long channel_try_recv(long long chan_ptr, long long out_ptr); -long long channel_has_data(long long chan_ptr); -long long channel_select(long long channels_list, long long timeout_ms); -long long ep_auto_to_string(long long val); - -typedef struct EpChannel_ { - long long* data; - long long capacity; - long long head; - long long tail; - long long size; - ep_mutex_t mutex; - ep_cond_t cond_recv; - ep_cond_t cond_send; -} EpChannel; - -/* Global channel registry — allows GC to scan values in-transit in channel buffers. - Without this, an object sent to a channel but not yet received has NO GC root: - the sender has popped it, the receiver hasn't pushed it, and the channel buffer - is not scanned. The GC sweeps it → receiver gets a dangling pointer. */ -#define EP_MAX_CHANNELS 1024 -static EpChannel* ep_channel_registry[EP_MAX_CHANNELS]; -static int ep_channel_count = 0; -static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void ep_register_channel(EpChannel* chan) { - pthread_mutex_lock(&ep_channel_registry_mutex); - if (ep_channel_count < EP_MAX_CHANNELS) { - ep_channel_registry[ep_channel_count++] = chan; - } - pthread_mutex_unlock(&ep_channel_registry_mutex); -} - -/* Channel scanning implementations — called by GC mark via function pointers. - These are defined here (after EpChannel) so they can access struct fields. */ -static void ep_gc_mark_object(void* ptr); /* forward decl */ -static void ep_gc_mark_object_minor(void* ptr); /* forward decl */ - -static void ep_gc_scan_channels_major_impl(void) { - pthread_mutex_lock(&ep_channel_registry_mutex); - for (int c = 0; c < ep_channel_count; c++) { - EpChannel* chan = ep_channel_registry[c]; - if (!chan || chan->size <= 0) continue; - ep_mutex_lock(&chan->mutex); - for (long long j = 0; j < chan->size; j++) { - long long idx = (chan->head + j) % chan->capacity; - long long val = chan->data[idx]; - if (val != 0) ep_gc_mark_object((void*)val); - } - ep_mutex_unlock(&chan->mutex); - } - pthread_mutex_unlock(&ep_channel_registry_mutex); -} - -static void ep_gc_scan_channels_minor_impl(void) { - pthread_mutex_lock(&ep_channel_registry_mutex); - for (int c = 0; c < ep_channel_count; c++) { - EpChannel* chan = ep_channel_registry[c]; - if (!chan || chan->size <= 0) continue; - ep_mutex_lock(&chan->mutex); - for (long long j = 0; j < chan->size; j++) { - long long idx = (chan->head + j) % chan->capacity; - long long val = chan->data[idx]; - if (val != 0) ep_gc_mark_object_minor((void*)val); - } - ep_mutex_unlock(&chan->mutex); - } - pthread_mutex_unlock(&ep_channel_registry_mutex); -} - -long long create_channel(void) { - EpChannel* chan = malloc(sizeof(EpChannel)); - if (!chan) return 0; - chan->capacity = 1024; - chan->data = malloc(chan->capacity * sizeof(long long)); - chan->head = 0; - chan->tail = 0; - chan->size = 0; - ep_mutex_init(&chan->mutex); - ep_cond_init(&chan->cond_recv); - ep_cond_init(&chan->cond_send); - ep_register_channel(chan); - return (long long)chan; -} - -long long send_channel(long long chan_ptr, long long value) { - EpChannel* chan = (EpChannel*)chan_ptr; - if (!chan) return 0; - /* Suppress GC during channel operations. The blocking condvar wait - can interleave with GC mark/sweep on another thread, causing - use-after-free when the GC sweeps objects that are live on a - thread currently blocked in send/receive. Channel buffers contain - raw long long values (not GC-tracked pointers), so suppressing - GC here is safe. */ - int gc_was_enabled = ep_gc_enabled; - ep_gc_enabled = 0; - ep_mutex_lock(&chan->mutex); - while (chan->size >= chan->capacity) { - ep_cond_wait(&chan->cond_send, &chan->mutex); - } - chan->data[chan->tail] = value; - chan->tail = (chan->tail + 1) % chan->capacity; - chan->size += 1; - ep_cond_signal(&chan->cond_recv); - ep_mutex_unlock(&chan->mutex); - ep_gc_enabled = gc_was_enabled; - return value; -} - -long long receive_channel(long long chan_ptr) { - EpChannel* chan = (EpChannel*)chan_ptr; - if (!chan) return 0; - /* Suppress GC during channel receive — same rationale as send_channel */ - int gc_was_enabled = ep_gc_enabled; - ep_gc_enabled = 0; - ep_mutex_lock(&chan->mutex); - while (chan->size <= 0) { - ep_cond_wait(&chan->cond_recv, &chan->mutex); - } - long long value = chan->data[chan->head]; - chan->head = (chan->head + 1) % chan->capacity; - chan->size -= 1; - ep_cond_signal(&chan->cond_send); - ep_mutex_unlock(&chan->mutex); - ep_gc_enabled = gc_was_enabled; - return value; -} - -// Non-blocking receive — returns 1 if data was available, 0 if channel empty -long long channel_try_recv(long long chan_ptr, long long out_ptr) { - EpChannel* chan = (EpChannel*)chan_ptr; - if (!chan) return 0; - ep_mutex_lock(&chan->mutex); - if (chan->size <= 0) { - ep_mutex_unlock(&chan->mutex); - return 0; - } - long long value = chan->data[chan->head]; - chan->head = (chan->head + 1) % chan->capacity; - chan->size -= 1; - ep_cond_signal(&chan->cond_send); - ep_mutex_unlock(&chan->mutex); - if (out_ptr) { - *((long long*)out_ptr) = value; - } - return 1; -} - -// Check if channel has data without consuming it -long long channel_has_data(long long chan_ptr) { - EpChannel* chan = (EpChannel*)chan_ptr; - if (!chan) return 0; - ep_mutex_lock(&chan->mutex); - int has = (chan->size > 0) ? 1 : 0; - ep_mutex_unlock(&chan->mutex); - return has; -} - -// Select: wait for any of N channels to have data, with timeout in ms -// channels_list is a list of channel pointers -// Returns index (0-based) of first ready channel, or -1 on timeout -long long channel_select(long long channels_list, long long timeout_ms) { - EpList* list = (EpList*)channels_list; - if (!list || list->length == 0) return -1; - -#ifdef _WIN32 - ULONGLONG start_tick = GetTickCount64(); -#else - struct timespec start, now; - clock_gettime(CLOCK_MONOTONIC, &start); -#endif - - while (1) { - // Poll all channels - for (long long i = 0; i < list->length; i++) { - EpChannel* chan = (EpChannel*)list->data[i]; - if (chan) { - ep_mutex_lock(&chan->mutex); - if (chan->size > 0) { - ep_mutex_unlock(&chan->mutex); - return i; - } - ep_mutex_unlock(&chan->mutex); - } - } - - // Check timeout - if (timeout_ms >= 0) { -#ifdef _WIN32 - ULONGLONG now_tick = GetTickCount64(); - long long elapsed = (long long)(now_tick - start_tick); -#else - clock_gettime(CLOCK_MONOTONIC, &now); - long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000; -#endif - if (elapsed >= timeout_ms) return -1; - } - - // Brief sleep to avoid busy-wait -#ifdef _WIN32 - Sleep(1); -#else - usleep(1000); // 1ms -#endif - } -} - -#ifdef __wasm__ -long long ep_net_connect(const char* host, long long port) { - (void)host; (void)port; - return -1; -} - -long long ep_net_listen(long long port) { - (void)port; - return -1; -} - -long long ep_net_accept(long long server_fd) { - (void)server_fd; - return -1; -} - -long long ep_net_send(long long fd, const char* data) { - (void)fd; (void)data; - return 0; -} - -char* ep_net_recv(long long fd, long long max_len) { - (void)fd; (void)max_len; - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - return empty; -} - -long long ep_net_close(long long fd) { - (void)fd; - return -1; -} - -long long ep_sleep_ms(long long ms) { - struct timespec ts; - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000000; - nanosleep(&ts, NULL); - return 0; -} - -long long ep_system(long long cmd) { - (void)cmd; - return -1; -} - -long long ep_play_sound(long long path) { - (void)path; - return -1; -} - -long long ep_dlopen(long long path) { - (void)path; - return 0; -} - -long long ep_dlsym(long long handle, long long name) { - (void)handle; (void)name; - return 0; -} - -long long ep_dlclose(long long handle) { - (void)handle; - return 0; -} -#else -long long ep_net_connect(const char* host, long long port) { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) return -1; - struct hostent* server = gethostbyname(host); - if (!server) { -#ifdef _WIN32 - closesocket(sockfd); -#else - close(sockfd); -#endif - return -1; - } - struct sockaddr_in serv_addr; - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length); - serv_addr.sin_port = htons(port); -#ifdef _WIN32 - if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { - closesocket(sockfd); - return -1; - } -#else - // Bounded connect: an unreachable peer must not block ~75s on the OS SYN - // timeout (this stalled node startup). Non-blocking connect + 5s select, then - // restore blocking mode for the rest of the session. - int _ep_flags = fcntl(sockfd, F_GETFL, 0); - fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK); - int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); - if (_ep_cr < 0) { - if (errno != EINPROGRESS) { close(sockfd); return -1; } - fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset); - struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0; - int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv); - if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error - int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err); - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) { - close(sockfd); - return -1; - } - } - fcntl(sockfd, F_SETFL, _ep_flags); -#endif - return sockfd; -} - -long long ep_net_listen(long long port) { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) return -1; - int opt = 1; - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); - struct sockaddr_in serv_addr; - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { -#ifdef _WIN32 - closesocket(sockfd); -#else - close(sockfd); -#endif - return -1; - } - if (listen(sockfd, 10) < 0) { -#ifdef _WIN32 - closesocket(sockfd); -#else - close(sockfd); -#endif - return -1; - } - return sockfd; -} - -long long ep_net_accept(long long server_fd) { - struct sockaddr_in cli_addr; - socklen_t clilen = sizeof(cli_addr); - int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen); - if (newsockfd >= 0) { - /* Bound how long a single recv/send may block so a slow or silent - client cannot pin a handler thread forever (slowloris). */ - struct timeval tv; - tv.tv_sec = 30; - tv.tv_usec = 0; - setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); - setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); - } - return newsockfd; -} - -long long ep_net_send(long long fd, const char* data) { - if (!data) return 0; - /* send() may write fewer bytes than requested (partial write under load/ - backpressure). A single send() therefore silently truncated large IPC - responses, cutting agent replies mid-stream. Loop until all bytes are sent. */ - size_t total = strlen(data); - size_t off = 0; - while (off < total) { - ssize_t n = send((int)fd, data + off, total - off, 0); - if (n <= 0) break; - off += (size_t)n; - } - return (long long)off; -} - -char* ep_net_recv(long long fd, long long max_len) { - char* buf = malloc(max_len + 1); - if (!buf) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - return empty; - } -#ifdef _WIN32 - int n = recv((int)fd, buf, (int)max_len, 0); -#else - ssize_t n = recv((int)fd, buf, max_len, 0); -#endif - if (n < 0) n = 0; - buf[n] = '\0'; - return buf; -} - -long long ep_net_close(long long fd) { -#ifdef _WIN32 - return closesocket((int)fd); -#else - return close((int)fd); -#endif -} - -long long ep_sleep_ms(long long ms) { -#ifdef _WIN32 - Sleep((DWORD)ms); -#else - usleep((useconds_t)(ms * 1000)); -#endif - return 0; -} - -long long ep_system(long long cmd) { - return (long long)system((const char*)cmd); -} - -long long ep_play_sound(long long path) { - char cmd[512]; - snprintf(cmd, sizeof(cmd), "afplay '%s' &", (const char*)path); - return (long long)system(cmd); -} - -/* ========== Dynamic Library Loading (FFI) ========== */ -#ifndef _WIN32 -#include -#endif - -long long ep_dlopen(long long path) { -#ifdef _WIN32 - HMODULE h = LoadLibraryA((const char*)path); - return (long long)h; -#else - const char* p = (const char*)path; - void* handle = dlopen(p, RTLD_LAZY); - return (long long)handle; -#endif -} - -long long ep_dlsym(long long handle, long long name) { -#ifdef _WIN32 - FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name); - return (long long)sym; -#else - void* sym = dlsym((void*)handle, (const char*)name); - return (long long)sym; -#endif -} - -long long ep_dlclose(long long handle) { -#ifdef _WIN32 - return (long long)FreeLibrary((HMODULE)handle); -#else - return (long long)dlclose((void*)handle); -#endif -} -#endif - -/* Call a function pointer with 0..6 arguments. - These are type-punned through long long — the C calling convention - makes this work for integer and pointer arguments. */ -typedef long long (*ep_fn0)(void); -typedef long long (*ep_fn1)(long long); -typedef long long (*ep_fn2)(long long, long long); -typedef long long (*ep_fn3)(long long, long long, long long); -typedef long long (*ep_fn4)(long long, long long, long long, long long); -typedef long long (*ep_fn5)(long long, long long, long long, long long, long long); -typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long); -typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long); -typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long); -typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long); -typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long); - -long long ep_dlcall0(long long fptr) { - return ((ep_fn0)fptr)(); -} -long long ep_dlcall1(long long fptr, long long a0) { - return ((ep_fn1)fptr)(a0); -} -long long ep_dlcall2(long long fptr, long long a0, long long a1) { - return ((ep_fn2)fptr)(a0, a1); -} -long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) { - return ((ep_fn3)fptr)(a0, a1, a2); -} -long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) { - return ((ep_fn4)fptr)(a0, a1, a2, a3); -} -long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) { - return ((ep_fn5)fptr)(a0, a1, a2, a3, a4); -} -long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) { - return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5); -} -long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) { - return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6); -} -long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) { - return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7); -} -long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) { - return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8); -} -long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) { - return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); -} - -/* ========== Float FFI: ep_dlcall_f* ========== */ -/* For calling C functions that accept/return double values. - Arguments are passed as long long (bit-punned doubles). - Return value is a double bit-punned back to long long. - Use ep_double_to_bits() / ep_bits_to_double() to convert. */ - -typedef union { long long i; double f; } ep_float_bits; - -static inline double ep_ll_to_double(long long v) { - ep_float_bits u; u.i = v; return u.f; -} -static inline long long ep_double_to_ll(double v) { - ep_float_bits u; u.f = v; return u.i; -} - -/* Convert between ErnosPlain float representation and raw bits */ -long long ep_double_to_bits(long long float_val) { - /* float_val is already an EP Float stored as long long bits */ - return float_val; -} -long long ep_bits_to_double(long long bits) { - return bits; -} - -/* Float function pointer typedefs */ -typedef double (*ep_ff0)(void); -typedef double (*ep_ff1)(double); -typedef double (*ep_ff2)(double, double); -typedef double (*ep_ff3)(double, double, double); -typedef double (*ep_ff4)(double, double, double, double); -typedef double (*ep_ff5)(double, double, double, double, double); -typedef double (*ep_ff6)(double, double, double, double, double, double); - -/* Call functions that take doubles and return double */ -long long ep_dlcall_f0(long long fptr) { - return ep_double_to_ll(((ep_ff0)fptr)()); -} -long long ep_dlcall_f1(long long fptr, long long a0) { - return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0))); -} -long long ep_dlcall_f2(long long fptr, long long a0, long long a1) { - return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1))); -} -long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) { - return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2))); -} -long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) { - return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3))); -} -long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) { - return ep_double_to_ll(((ep_ff5)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4))); -} -long long ep_dlcall_f6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) { - return ep_double_to_ll(((ep_ff6)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4), ep_ll_to_double(a5))); -} - -/* Variants that take doubles but return int (for comparison functions etc.) */ -typedef long long (*ep_fdi1)(double); -typedef long long (*ep_fdi2)(double, double); -typedef long long (*ep_fdi3)(double, double, double); - -long long ep_dlcall_fd1(long long fptr, long long a0) { - return ((ep_fdi1)fptr)(ep_ll_to_double(a0)); -} -long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) { - return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)); -} -long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) { - return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)); -} -/* ========== End Float FFI ========== */ -/* ========== End Dynamic Library Loading ========== */ - -unsigned long hash_string(const char* str) { - unsigned long hash = 5381; - int c; - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; - } - return hash; -} - -typedef struct { - char* key; - long long value; - int used; -} EpMapEntry; - -typedef struct { - EpMapEntry* entries; - long long capacity; - long long size; -} EpMap; - -/* Map value traversal for GC — walks all entries and marks values. - Called by ep_gc_mark_object() via function pointer. */ -static void ep_gc_mark_map_values_impl(void* ptr) { - EpMap* map = (EpMap*)ptr; - if (!map || !map->entries) return; - for (long long i = 0; i < map->capacity; i++) { - if (map->entries[i].used && map->entries[i].value != 0) { - ep_gc_mark_object((void*)map->entries[i].value); - } - /* Also mark keys if they are heap strings */ - if (map->entries[i].used && map->entries[i].key != NULL) { - ep_gc_mark_object((void*)map->entries[i].key); - } - } -} - -static void ep_gc_mark_map_values_minor_impl(void* ptr) { - EpMap* map = (EpMap*)ptr; - if (!map || !map->entries) return; - for (long long i = 0; i < map->capacity; i++) { - if (map->entries[i].used && map->entries[i].value != 0) { - ep_gc_mark_object_minor((void*)map->entries[i].value); - } - if (map->entries[i].used && map->entries[i].key != NULL) { - ep_gc_mark_object_minor((void*)map->entries[i].key); - } - } -} - -long long create_map(void) { - EpMap* map = malloc(sizeof(EpMap)); - if (!map) return 0; - map->capacity = 16; - map->size = 0; - map->entries = calloc(map->capacity, sizeof(EpMapEntry)); - if (!map->entries) { - free(map); - return 0; - } - ep_gc_register(map, EP_OBJ_MAP); - return (long long)map; -} - -static void map_resize(EpMap* map, long long new_capacity) { - EpMapEntry* old_entries = map->entries; - long long old_capacity = map->capacity; - map->capacity = new_capacity; - map->entries = calloc(new_capacity, sizeof(EpMapEntry)); - map->size = 0; - for (long long i = 0; i < old_capacity; i++) { - if (old_entries[i].used && old_entries[i].key != NULL) { - char* key = old_entries[i].key; - long long value = old_entries[i].value; - unsigned long h = hash_string(key) % new_capacity; - while (map->entries[h].used) { - h = (h + 1) % new_capacity; - } - map->entries[h].key = key; - map->entries[h].value = value; - map->entries[h].used = 1; - map->size++; - } - } - free(old_entries); -} - -/* Convert a key value to a string — handles both string pointers and integers */ -static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) { - if (key_val == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; } - /* Check if value is in plausible pointer range for a string */ - if (key_val > 0x100000) { - const char* p = (const char*)(void*)key_val; - unsigned char first = (unsigned char)*p; - if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) { - return p; /* valid string pointer */ - } - } - snprintf(buf, bufsize, "%lld", key_val); - return buf; -} - -long long map_insert(long long map_ptr, long long key_val, long long value) { - if (EP_BADPTR(map_ptr)) return 0; - EpMap* map = (EpMap*)map_ptr; - char keybuf[32]; - const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); - if (!map) return 0; - if (map->size * 2 >= map->capacity) { - map_resize(map, map->capacity * 2); - } - unsigned long h = hash_string(key) % map->capacity; - while (map->entries[h].used) { - if (strcmp(map->entries[h].key, key) == 0) { - map->entries[h].value = value; - ep_gc_write_barrier((void*)map_ptr, value); - return value; - } - h = (h + 1) % map->capacity; - } - map->entries[h].key = strdup(key); - map->entries[h].value = value; - map->entries[h].used = 1; - map->size++; - ep_gc_write_barrier((void*)map_ptr, value); - return value; -} - -long long map_get_val(long long map_ptr, long long key_val) { - if (EP_BADPTR(map_ptr)) return 0; - EpMap* map = (EpMap*)map_ptr; - char keybuf[32]; - const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); - if (!map) return 0; - unsigned long h = hash_string(key) % map->capacity; - long long start_h = h; - while (map->entries[h].used) { - if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { - return map->entries[h].value; - } - h = (h + 1) % map->capacity; - if (h == start_h) break; - } - return 0; -} - -/* map_set_str: store a string value (strdup'd copy) under a string key */ -long long map_set_str(long long map_ptr, long long key_val, long long str_val) { - /* Store the string pointer as a long long value — same as map_insert */ - return map_insert(map_ptr, key_val, str_val); -} - -/* map_get_str: retrieve a string value from a map (returns char* as long long) */ -long long map_get_str(long long map_ptr, long long key_val) { - /* Same as map_get_val — the stored long long IS a char* pointer */ - return map_get_val(map_ptr, key_val); -} - -long long map_contains(long long map_ptr, long long key_val) { - if (EP_BADPTR(map_ptr)) return 0; - EpMap* map = (EpMap*)map_ptr; - char keybuf[32]; - const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); - if (!map) return 0; - unsigned long h = hash_string(key) % map->capacity; - long long start_h = h; - while (map->entries[h].used) { - if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { - return 1; - } - h = (h + 1) % map->capacity; - if (h == start_h) break; - } - return 0; -} - -long long map_delete(long long map_ptr, long long key_val) { - if (EP_BADPTR(map_ptr)) return 0; - EpMap* map = (EpMap*)map_ptr; - char keybuf[32]; - const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); - if (!map) return 0; - unsigned long h = hash_string(key) % map->capacity; - long long start_h = h; - while (map->entries[h].used) { - if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { - free(map->entries[h].key); - map->entries[h].key = NULL; - map->entries[h].value = 0; - map->entries[h].used = 0; - map->size--; - long long next_h = (h + 1) % map->capacity; - while (map->entries[next_h].used) { - char* k = map->entries[next_h].key; - long long v = map->entries[next_h].value; - map->entries[next_h].key = NULL; - map->entries[next_h].value = 0; - map->entries[next_h].used = 0; - map->size--; - map_insert(map_ptr, (long long)k, v); - free(k); - next_h = (next_h + 1) % map->capacity; - } - return 1; - } - h = (h + 1) % map->capacity; - if (h == start_h) break; - } - return 0; -} - -long long map_keys(long long map_ptr) { - EpMap* map = (EpMap*)map_ptr; - if (!map) return (long long)create_list(); - long long list = create_list(); - for (long long i = 0; i < map->capacity; i++) { - if (map->entries[i].used && map->entries[i].key) { - append_list(list, (long long)strdup(map->entries[i].key)); - } - } - return list; -} - -long long map_values(long long map_ptr) { - EpMap* map = (EpMap*)map_ptr; - if (!map) return (long long)create_list(); - long long list = create_list(); - for (long long i = 0; i < map->capacity; i++) { - if (map->entries[i].used && map->entries[i].key) { - append_list(list, map->entries[i].value); - } - } - return list; -} - -long long map_size(long long map_ptr) { - EpMap* map = (EpMap*)map_ptr; - if (!map) return 0; - return map->size; -} - -long long free_map(long long map_ptr) { - EpMap* map = (EpMap*)map_ptr; - if (!map) return 0; - /* Skip if already freed (idempotent) */ - if (!ep_gc_find(map)) return 0; - ep_gc_unregister(map); - for (long long i = 0; i < map->capacity; i++) { - if (map->entries[i].used && map->entries[i].key != NULL) { - free(map->entries[i].key); - } - } - free(map->entries); - free(map); - return 0; -} - -typedef struct { - long long* data; - long long capacity; - long long head; - long long tail; - long long size; -} EpDeque; - -long long create_deque(void) { - EpDeque* dq = malloc(sizeof(EpDeque)); - if (!dq) return 0; - dq->capacity = 16; - dq->size = 0; - dq->head = 0; - dq->tail = 0; - dq->data = malloc(dq->capacity * sizeof(long long)); - if (!dq->data) { - free(dq); - return 0; - } - return (long long)dq; -} - -static void deque_resize(EpDeque* dq, long long new_capacity) { - long long* new_data = malloc(new_capacity * sizeof(long long)); - for (long long i = 0; i < dq->size; i++) { - new_data[i] = dq->data[(dq->head + i) % dq->capacity]; - } - free(dq->data); - dq->data = new_data; - dq->capacity = new_capacity; - dq->head = 0; - dq->tail = dq->size; -} - -long long deque_push_back(long long dq_ptr, long long value) { - EpDeque* dq = (EpDeque*)dq_ptr; - if (!dq) return 0; - if (dq->size >= dq->capacity) { - deque_resize(dq, dq->capacity * 2); - } - dq->data[dq->tail] = value; - dq->tail = (dq->tail + 1) % dq->capacity; - dq->size++; - return value; -} - -long long deque_push_front(long long dq_ptr, long long value) { - EpDeque* dq = (EpDeque*)dq_ptr; - if (!dq) return 0; - if (dq->size >= dq->capacity) { - deque_resize(dq, dq->capacity * 2); - } - dq->head = (dq->head - 1 + dq->capacity) % dq->capacity; - dq->data[dq->head] = value; - dq->size++; - return value; -} - -long long deque_pop_back(long long dq_ptr) { - EpDeque* dq = (EpDeque*)dq_ptr; - if (!dq || dq->size == 0) return 0; - dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity; - long long value = dq->data[dq->tail]; - dq->size--; - return value; -} - -long long deque_pop_front(long long dq_ptr) { - EpDeque* dq = (EpDeque*)dq_ptr; - if (!dq || dq->size == 0) return 0; - long long value = dq->data[dq->head]; - dq->head = (dq->head + 1) % dq->capacity; - dq->size--; - return value; -} - -long long deque_length(long long dq_ptr) { - EpDeque* dq = (EpDeque*)dq_ptr; - if (!dq) return 0; - return dq->size; -} - -long long free_deque(long long dq_ptr) { - EpDeque* dq = (EpDeque*)dq_ptr; - if (!dq) return 0; - free(dq->data); - free(dq); - return 0; -} - -/* Filesystem Operations */ -#include -#include - -long long fs_scan_dir(long long path_val) { - const char* path = (const char*)path_val; - long long list_ptr = create_list(); - if (!path) return list_ptr; - DIR* d = opendir(path); - if (!d) return list_ptr; - struct dirent* dir; - while ((dir = readdir(d)) != NULL) { - if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { - continue; - } - char* name = strdup(dir->d_name); - append_list(list_ptr, (long long)name); - } - closedir(d); - return list_ptr; -} - -long long fs_copy_file(long long src_val, long long dest_val) { - const char* src = (const char*)src_val; - const char* dest = (const char*)dest_val; - if (!src || !dest) return 0; - FILE* f_src = fopen(src, "rb"); - if (!f_src) return 0; - FILE* f_dest = fopen(dest, "wb"); - if (!f_dest) { - fclose(f_src); - return 0; - } - char buf[4096]; - size_t n; - while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) { - fwrite(buf, 1, n, f_dest); - } - fclose(f_src); - fclose(f_dest); - return 1; -} - -long long fs_delete_file(long long path_val) { - const char* path = (const char*)path_val; - if (!path) return 0; - return remove(path) == 0 ? 1 : 0; -} - -long long fs_move_file(long long src_val, long long dest_val) { - const char* src = (const char*)src_val; - const char* dest = (const char*)dest_val; - if (!src || !dest) return 0; - return rename(src, dest) == 0 ? 1 : 0; -} - -long long fs_exists(long long path_val) { - const char* path = (const char*)path_val; - if (!path) return 0; - struct stat st; - return stat(path, &st) == 0 ? 1 : 0; -} - -long long fs_is_dir(long long path_val) { - const char* path = (const char*)path_val; - if (!path) return 0; - struct stat st; - if (stat(path, &st) != 0) return 0; - return S_ISDIR(st.st_mode) ? 1 : 0; -} - -long long fs_is_file(long long path_val) { - const char* path = (const char*)path_val; - if (!path) return 0; - struct stat st; - if (stat(path, &st) != 0) return 0; - return S_ISREG(st.st_mode) ? 1 : 0; -} - -long long fs_get_size(long long path_val) { - const char* path = (const char*)path_val; - if (!path) return 0; - struct stat st; - if (stat(path, &st) != 0) return 0; - return (long long)st.st_size; -} - -/* HTTP Client */ -#ifdef __wasm__ -long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) { - (void)method_val; (void)url_val; (void)headers_val; (void)body_val; - return (long long)strdup("Error: HTTP request is not supported on WebAssembly"); -} -#else -long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) { - const char* method = (const char*)method_val; - const char* url = (const char*)url_val; - const char* headers = (const char*)headers_val; - const char* body = (const char*)body_val; - if (!method || !url) return (long long)strdup(""); - if (strncmp(url, "http://", 7) != 0) { - return (long long)strdup("Error: only http:// protocol supported"); - } - const char* host_start = url + 7; - const char* path_start = strchr(host_start, '/'); - char host[256]; - char path[1024]; - if (path_start) { - size_t host_len = path_start - host_start; - if (host_len >= sizeof(host)) host_len = sizeof(host) - 1; - strncpy(host, host_start, host_len); - host[host_len] = '\0'; - strncpy(path, path_start, sizeof(path) - 1); - path[sizeof(path) - 1] = '\0'; - } else { - strncpy(host, host_start, sizeof(host) - 1); - host[sizeof(host) - 1] = '\0'; - strcpy(path, "/"); - } - int port = 80; - char* colon = strchr(host, ':'); - if (colon) { - *colon = '\0'; - port = atoi(colon + 1); - } - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) return (long long)strdup("Error: socket creation failed"); - struct hostent* server = gethostbyname(host); - if (!server) { - close(sockfd); - return (long long)strdup("Error: host resolution failed"); - } - struct sockaddr_in serv_addr; - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length); - serv_addr.sin_port = htons(port); - if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { - close(sockfd); - return (long long)strdup("Error: connection failed"); - } - char req[4096]; - size_t body_len = body ? strlen(body) : 0; - int req_len = snprintf(req, sizeof(req), - "%s %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Content-Length: %zu\r\n" - "Connection: close\r\n" - "%s%s" - "\r\n", - method, path, host, body_len, headers ? headers : "", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\n') ? "\r\n" : ""); - if (send(sockfd, req, req_len, 0) < 0) { - close(sockfd); - return (long long)strdup("Error: send failed"); - } - if (body_len > 0) { - if (send(sockfd, body, body_len, 0) < 0) { - close(sockfd); - return (long long)strdup("Error: send body failed"); - } - } - size_t resp_cap = 4096; - size_t resp_len = 0; - char* resp = malloc(resp_cap); - if (!resp) { - close(sockfd); - return (long long)strdup(""); - } - char recv_buf[4096]; - ssize_t n; - while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) { - if (resp_len + n >= resp_cap) { - resp_cap *= 2; - char* new_resp = realloc(resp, resp_cap); - if (!new_resp) { - free(resp); - close(sockfd); - return (long long)strdup("Error: memory allocation failed"); - } - resp = new_resp; - } - memcpy(resp + resp_len, recv_buf, n); - resp_len += n; - } - resp[resp_len] = '\0'; - close(sockfd); - // Strip HTTP headers — return only the body after \r\n\r\n - char* http_body = strstr(resp, "\r\n\r\n"); - if (http_body) { - http_body += 4; - char* result = strdup(http_body); - free(resp); - return (long long)result; - } - return (long long)resp; -} -#endif - -#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits)))) -#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) -#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) -#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) -#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) -#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) - -typedef struct { - unsigned char data[64]; - unsigned int datalen; - unsigned long long bitlen; - unsigned int state[8]; -} EP_SHA256_CTX; - -static const unsigned int sha256_k[64] = { - 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, - 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, - 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, - 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, - 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, - 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, - 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, - 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 -}; - -void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) { - unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; - for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); - for ( ; i < 64; ++i) - m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; - a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; - e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; - for (i = 0; i < 64; ++i) { - t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i]; - t2 = EP0(a) + MAJ(a,b,c); - h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; - } - ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; - ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; -} - -void ep_sha256_init(EP_SHA256_CTX *ctx) { - ctx->datalen = 0; ctx->bitlen = 0; - ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; -} - -void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) { - for (size_t i = 0; i < len; ++i) { - ctx->data[ctx->datalen] = data[i]; - ctx->datalen++; - if (ctx->datalen == 64) { - ep_sha256_transform(ctx, ctx->data); - ctx->bitlen += 512; - ctx->datalen = 0; - } - } -} - -void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) { - unsigned int i = ctx->datalen; - if (ctx->datalen < 56) { - ctx->data[i++] = 0x80; - while (i < 56) ctx->data[i++] = 0x00; - } else { - ctx->data[i++] = 0x80; - while (i < 64) ctx->data[i++] = 0x00; - ep_sha256_transform(ctx, ctx->data); - memset(ctx->data, 0, 56); - } - ctx->bitlen += ctx->datalen * 8; - ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; - ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; - ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; - ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; - ep_sha256_transform(ctx, ctx->data); - for (i = 0; i < 4; ++i) { - hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; - hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; - hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; - hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; - hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; - hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; - hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; - hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; - } -} - -char* ep_sha256(const char* s) { - if (!s) s = ""; - EP_SHA256_CTX ctx; - ep_sha256_init(&ctx); - ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s)); - unsigned char hash[32]; - ep_sha256_final(&ctx, hash); - char* result = malloc(65); - if (result) { - for (int i = 0; i < 32; i++) { - snprintf(result + (i * 2), 3, "%02x", hash[i]); - } - result[64] = '\0'; - } - return result; -} - -/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary - safe), so keys/messages containing NUL bytes hash correctly. Returns a - malloc'd 64-char lowercase hex string. */ -long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) { - const unsigned char* key = (const unsigned char*)key_ptr; - const unsigned char* msg = (const unsigned char*)msg_ptr; - size_t klen = (size_t)key_len; - size_t mlen = (size_t)msg_len; - - unsigned char k0[64]; - memset(k0, 0, sizeof(k0)); - if (klen > 64) { - /* Keys longer than the block size are replaced by their hash. */ - EP_SHA256_CTX kc; - ep_sha256_init(&kc); - ep_sha256_update(&kc, key ? key : (const unsigned char*)"", klen); - unsigned char kh[32]; - ep_sha256_final(&kc, kh); - memcpy(k0, kh, 32); - } else if (key) { - memcpy(k0, key, klen); - } - - unsigned char ipad[64], opad[64]; - for (int i = 0; i < 64; i++) { - ipad[i] = k0[i] ^ 0x36; - opad[i] = k0[i] ^ 0x5c; - } - - /* inner = H((K0 ^ ipad) || message) */ - EP_SHA256_CTX ic; - ep_sha256_init(&ic); - ep_sha256_update(&ic, ipad, 64); - if (msg && mlen) ep_sha256_update(&ic, msg, mlen); - unsigned char inner[32]; - ep_sha256_final(&ic, inner); - - /* mac = H((K0 ^ opad) || inner) */ - EP_SHA256_CTX oc; - ep_sha256_init(&oc); - ep_sha256_update(&oc, opad, 64); - ep_sha256_update(&oc, inner, 32); - unsigned char mac[32]; - ep_sha256_final(&oc, mac); - - char* out = (char*)malloc(65); - if (out) { - for (int i = 0; i < 32; i++) { - snprintf(out + (i * 2), 3, "%02x", mac[i]); - } - out[64] = '\0'; - } - return (long long)out; -} - -typedef struct { - unsigned int count[2]; - unsigned int state[4]; - unsigned char buffer[64]; -} EP_MD5_CTX; - -#define F(x,y,z) (((x) & (y)) | (~(x) & (z))) -#define G(x,y,z) (((x) & (z)) | ((y) & ~(z))) -#define H(x,y,z) ((x) ^ (y) ^ (z)) -#define I(x,y,z) ((y) ^ ((x) | ~(z))) -#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n)))) - -#define FF(a,b,c,d,x,s,ac) { \ - (a) += F((b),(c),(d)) + (x) + (ac); \ - (a) = ROTATE_LEFT((a),(s)); \ - (a) += (b); \ -} -#define GG(a,b,c,d,x,s,ac) { \ - (a) += G((b),(c),(d)) + (x) + (ac); \ - (a) = ROTATE_LEFT((a),(s)); \ - (a) += (b); \ -} -#define HH(a,b,c,d,x,s,ac) { \ - (a) += H((b),(c),(d)) + (x) + (ac); \ - (a) = ROTATE_LEFT((a),(s)); \ - (a) += (b); \ -} -#define II(a,b,c,d,x,s,ac) { \ - (a) += I((b),(c),(d)) + (x) + (ac); \ - (a) = ROTATE_LEFT((a),(s)); \ - (a) += (b); \ -} - -void ep_md5_init(EP_MD5_CTX *ctx) { - ctx->count[0] = ctx->count[1] = 0; - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xefcdab89; - ctx->state[2] = 0x98badcfe; - ctx->state[3] = 0x10325476; -} - -void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) { - unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - for (int i = 0, j = 0; i < 16; i++, j += 4) - x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24); - - FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee); - FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501); - FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be); - FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821); - - GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); - GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); - GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed); - GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); - - HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c); - HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70); - HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05); - HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665); - - II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039); - II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1); - II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1); - II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391); - - state[0] += a; state[1] += b; state[2] += c; state[3] += d; -} - -void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) { - unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index; - ctx->count[0] += input_len << 3; - if (ctx->count[0] < (input_len << 3)) ctx->count[1]++; - ctx->count[1] += input_len >> 29; - if (input_len >= part_len) { - memcpy(&ctx->buffer[index], input, part_len); - ep_md5_transform(ctx->state, ctx->buffer); - for (i = part_len; i + 63 < input_len; i += 64) - ep_md5_transform(ctx->state, &input[i]); - index = 0; - } - memcpy(&ctx->buffer[index], &input[i], input_len - i); -} - -void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) { - unsigned char bits[8]; - bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24; - bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24; - unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index); - unsigned char padding[64]; - memset(padding, 0, 64); padding[0] = 0x80; - ep_md5_update(ctx, padding, pad_len); - ep_md5_update(ctx, bits, 8); - for (int i = 0; i < 4; i++) { - digest[i*4] = ctx->state[i]; - digest[i*4 + 1] = ctx->state[i] >> 8; - digest[i*4 + 2] = ctx->state[i] >> 16; - digest[i*4 + 3] = ctx->state[i] >> 24; - } -} - -char* ep_md5(const char* s) { - if (!s) s = ""; - EP_MD5_CTX ctx; - ep_md5_init(&ctx); - ep_md5_update(&ctx, (const unsigned char*)s, strlen(s)); - unsigned char hash[16]; - ep_md5_final(&ctx, hash); - char* result = malloc(33); - if (result) { - for (int i = 0; i < 16; i++) { - snprintf(result + (i * 2), 3, "%02x", hash[i]); - } - result[32] = '\0'; - } - return result; -} - -char* read_file_content(const char* filepath) { - char mode[3]; - mode[0] = 'r'; - mode[1] = 'b'; - mode[2] = '\0'; - FILE* f = fopen(filepath, mode); - if (!f) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - ep_gc_register(empty, EP_OBJ_STRING); - return empty; - } - fseek(f, 0, SEEK_END); - long size = ftell(f); - fseek(f, 0, SEEK_SET); - char* buf = malloc(size + 1); - if (!buf) { - fclose(f); - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - ep_gc_register(empty, EP_OBJ_STRING); - return empty; - } - size_t read_bytes = fread(buf, 1, size, f); - buf[read_bytes] = '\0'; - fclose(f); - ep_gc_register(buf, EP_OBJ_STRING); - return buf; -} - -long long string_length(const char* s) { - if (!s) return 0; - return strlen(s); -} - -long long get_character(const char* s, long long index) { - if (!s) return 0; - long long len = strlen(s); - if (index < 0 || index >= len) return 0; - return (unsigned char)s[index]; -} - -long long create_list(void) { - EpList* list = malloc(sizeof(EpList)); - if (!list) return 0; - list->capacity = 4; - list->length = 0; - list->data = malloc(list->capacity * sizeof(long long)); - ep_gc_register(list, EP_OBJ_LIST); - return (long long)list; -} - -long long get_list_data_ptr(long long list_ptr) { - if (EP_BADPTR(list_ptr)) return 0; - EpList* list = (EpList*)list_ptr; - if (!list) return 0; - return (long long)list->data; -} - -long long append_list(long long list_ptr, long long value) { - if (EP_BADPTR(list_ptr)) return 0; - EpList* list = (EpList*)list_ptr; - if (!list) return 0; - if (list->length >= list->capacity) { - list->capacity *= 2; - list->data = realloc(list->data, list->capacity * sizeof(long long)); - } - list->data[list->length] = value; - list->length += 1; - ep_gc_write_barrier((void*)list_ptr, value); - return value; -} - -long long get_list(long long list_ptr, long long index) { - if (EP_BADPTR(list_ptr)) return 0; - EpList* list = (EpList*)list_ptr; - if (index < 0 || index >= list->length) return 0; - return list->data[index]; -} - -long long set_list(long long list_ptr, long long index, long long value) { - if (EP_BADPTR(list_ptr)) return 0; - EpList* list = (EpList*)list_ptr; - if (index < 0 || index >= list->length) return 0; - list->data[index] = value; - ep_gc_write_barrier((void*)list_ptr, value); - return value; -} - -long long length_list(long long list_ptr) { - if (EP_BADPTR(list_ptr)) return 0; - EpList* list = (EpList*)list_ptr; - return list->length; -} - -long long free_list(long long list_ptr) { - EpList* list = (EpList*)list_ptr; - if (!list) return 0; - /* Skip if already freed (idempotent) */ - if (!ep_gc_find(list)) return 0; - ep_gc_unregister(list); - free(list->data); - free(list); - return 0; -} - -static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) { - EpList* rows = (EpList*)arg; - EpList* row = (EpList*)create_list(); - for (int i = 0; i < argc; i++) { - char* val = argv[i] ? strdup(argv[i]) : strdup(""); - append_list((long long)row, (long long)val); - } - append_list((long long)rows, (long long)row); - return 0; -} - -long long sqlite_get_callback_ptr(long long dummy) { - return (long long)sqlite_list_callback; -} - -/* SQLite type-safe wrappers — marshal between int and long long */ -#ifdef EP_HAS_SQLITE -typedef struct sqlite3 sqlite3; -int sqlite3_open(const char*, sqlite3**); -int sqlite3_close(sqlite3*); -int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**); - -long long ep_sqlite3_open(long long filename, long long db_ptr) { - sqlite3* db = NULL; - int rc = sqlite3_open((const char*)filename, &db); - if (rc == 0 && db_ptr != 0) { - *((long long*)db_ptr) = (long long)db; - } - return (long long)rc; -} - -long long ep_sqlite3_close(long long db) { - return (long long)sqlite3_close((sqlite3*)db); -} - -long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) { - return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql, - (int(*)(void*,int,char**,char**))(callback), - (void*)cb_arg, (char**)errmsg_ptr); -} - -/* Prepared-statement API for parameterized queries (defeats SQL injection). */ -typedef struct sqlite3_stmt sqlite3_stmt; -int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**); -int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*)); -int sqlite3_bind_int64(sqlite3_stmt*, int, long long); -int sqlite3_step(sqlite3_stmt*); -int sqlite3_column_count(sqlite3_stmt*); -const unsigned char* sqlite3_column_text(sqlite3_stmt*, int); -long long sqlite3_column_int64(sqlite3_stmt*, int); -int sqlite3_finalize(sqlite3_stmt*); - -long long ep_sqlite3_prepare_v2(long long db, long long sql) { - sqlite3_stmt* stmt = NULL; - int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL); - if (rc != 0) return 0; - return (long long)stmt; -} - -long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) { - /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is - a bound parameter, never concatenated into SQL — this is the safe path. */ - return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx, - (const char*)value, -1, (void(*)(void*))(intptr_t)-1); -} - -long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) { - return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value); -} - -long long ep_sqlite3_step(long long stmt) { - return (long long)sqlite3_step((sqlite3_stmt*)stmt); -} - -long long ep_sqlite3_column_count(long long stmt) { - return (long long)sqlite3_column_count((sqlite3_stmt*)stmt); -} - -long long ep_sqlite3_column_text(long long stmt, long long col) { - const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col); - if (!t) return (long long)strdup(""); - return (long long)strdup((const char*)t); -} - -long long ep_sqlite3_column_int(long long stmt, long long col) { - return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col); -} - -long long ep_sqlite3_finalize(long long stmt) { - return (long long)sqlite3_finalize((sqlite3_stmt*)stmt); -} -#endif /* EP_HAS_SQLITE */ - -int ep_argc = 0; -char** ep_argv = NULL; - -void init_ep_args(int argc, char** argv) { - ep_argc = argc; - ep_argv = argv; - ep_gc_register_thread((void*)&argc); - /* Wire up channel scanning for GC (defined after EpChannel struct) */ - ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl; - ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl; - /* Wire up map value traversal for GC (defined after EpMap struct) */ - ep_gc_mark_map_values = ep_gc_mark_map_values_impl; - ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl; -} - -long long get_argument_count(void) { - return ep_argc; -} - -const char* get_argument(long long index) { - if (index < 0 || index >= ep_argc) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - return empty; - } - return ep_argv[index]; -} - -long long write_file_content(const char* filepath, const char* content) { - char mode[3]; - mode[0] = 'w'; - mode[1] = 'b'; - mode[2] = '\0'; - FILE* f = fopen(filepath, mode); - if (!f) return 0; - size_t len = strlen(content); - size_t written = fwrite(content, 1, len, f); - fclose(f); - return written == len ? 1 : 0; -} - -long long run_command(const char* command) { - if (!command) return -1; - return system(command); -} - -char* substring(const char* s, long long start, long long len) { - if (!s) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - ep_gc_register(empty, EP_OBJ_STRING); - return empty; - } - long long total_len = strlen(s); - if (start < 0 || start >= total_len || len <= 0) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - ep_gc_register(empty, EP_OBJ_STRING); - return empty; - } - if (start + len > total_len) { - len = total_len - start; - } - char* sub = malloc(len + 1); - if (!sub) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - ep_gc_register(empty, EP_OBJ_STRING); - return empty; - } - strncpy(sub, s + start, len); - sub[len] = '\0'; - ep_gc_register(sub, EP_OBJ_STRING); - return sub; -} - -char* string_from_list(long long list_ptr) { - EpList* list = (EpList*)list_ptr; - if (!list) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - ep_gc_register(empty, EP_OBJ_STRING); - return empty; - } - char* s = malloc(list->length + 1); - if (!s) { - char* empty = malloc(1); - if (empty) empty[0] = '\0'; - ep_gc_register(empty, EP_OBJ_STRING); - return empty; - } - for (long long i = 0; i < list->length; i++) { - s[i] = (char)list->data[i]; - } - s[list->length] = '\0'; - ep_gc_register(s, EP_OBJ_STRING); - return s; -} - -// Inverse of string_from_list: convert a string to a list of its byte values in -// a single O(n) pass (one strlen + one copy). This lets callers iterate a string -// in O(n) total via O(1) get_list, instead of O(n) get_character per index -// (which is O(n^2) over the whole string). -long long string_to_list(const char* s) { - EpList* list = malloc(sizeof(EpList)); - if (!list) return 0; - long long len = s ? (long long)strlen(s) : 0; - list->capacity = len > 0 ? len : 4; - list->length = len; - list->data = malloc(list->capacity * sizeof(long long)); - if (!list->data) { - list->capacity = 0; - list->length = 0; - ep_gc_register(list, EP_OBJ_LIST); - return (long long)list; - } - for (long long i = 0; i < len; i++) { - list->data[i] = (unsigned char)s[i]; - } - ep_gc_register(list, EP_OBJ_LIST); - return (long long)list; -} - -long long pop_list(long long list_ptr) { - if (EP_BADPTR(list_ptr)) return 0; - EpList* list = (EpList*)list_ptr; - if (!list || list->length <= 0) return 0; - list->length -= 1; - return list->data[list->length]; -} - -long long remove_list(long long list_ptr, long long index) { - if (EP_BADPTR(list_ptr)) return 0; - EpList* list = (EpList*)list_ptr; - if (!list || index < 0 || index >= list->length) return 0; - long long removed = list->data[index]; - for (long long i = index; i < list->length - 1; i++) { - list->data[i] = list->data[i + 1]; - } - list->length -= 1; - return removed; -} - -long long display_string(const char* s) { - if (s) puts(s); - return 0; -} - -/* ========== File System Runtime ========== */ -#include -#ifdef _WIN32 - #include - #include - #define mkdir(p, m) _mkdir(p) - #define rmdir _rmdir - #define getcwd _getcwd - #define popen _popen - #define pclose _pclose - #define getpid _getpid - #define setenv(k, v, o) _putenv_s(k, v) - /* Minimal dirent polyfill for Windows */ - #include - typedef struct { char d_name[260]; } ep_dirent; - typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR; - static EP_DIR* ep_opendir(const char* p) { - EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR)); - char buf[270]; snprintf(buf, sizeof(buf), "%s\\*", p); - d->hFind = FindFirstFileA(buf, &d->data); - d->first = 1; - return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d; - } - static ep_dirent* ep_readdir(EP_DIR* d) { - static ep_dirent ent; - if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; } - if (!FindNextFileA(d->hFind, &d->data)) return NULL; - strcpy(ent.d_name, d->data.cFileName); return &ent; - } - static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); } - #define DIR EP_DIR - #define dirent ep_dirent - #define opendir ep_opendir - #define readdir ep_readdir - #define closedir ep_closedir -#else - #include - #include -#endif - -long long ep_read_file(long long path_ptr) { - const char* path = (const char*)path_ptr; - FILE* f = fopen(path, "rb"); - if (!f) return (long long)""; - fseek(f, 0, SEEK_END); - long size = ftell(f); - fseek(f, 0, SEEK_SET); - char* buf = (char*)malloc(size + 1); - fread(buf, 1, size, f); - buf[size] = '\0'; - fclose(f); - return (long long)buf; -} - -long long ep_write_file(long long path_ptr, long long content_ptr) { - const char* path = (const char*)path_ptr; - const char* content = (const char*)content_ptr; - FILE* f = fopen(path, "wb"); - if (!f) return 0; - fputs(content, f); - fclose(f); - return 1; -} - -long long ep_append_file(long long path_ptr, long long content_ptr) { - const char* path = (const char*)path_ptr; - const char* content = (const char*)content_ptr; - FILE* f = fopen(path, "ab"); - if (!f) return 0; - fputs(content, f); - fclose(f); - return 1; -} - -long long ep_file_exists(long long path_ptr) { - const char* path = (const char*)path_ptr; - struct stat st; - return stat(path, &st) == 0 ? 1 : 0; -} - -long long ep_is_directory(long long path_ptr) { - const char* path = (const char*)path_ptr; - struct stat st; - if (stat(path, &st) != 0) return 0; - return S_ISDIR(st.st_mode) ? 1 : 0; -} - -long long ep_file_size(long long path_ptr) { - const char* path = (const char*)path_ptr; - struct stat st; - if (stat(path, &st) != 0) return -1; - return (long long)st.st_size; -} - -long long ep_list_directory(long long path_ptr) { - const char* path = (const char*)path_ptr; - DIR* dir = opendir(path); - if (!dir) return (long long)create_list(); - long long list = create_list(); - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || - (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) continue; - char* name = strdup(entry->d_name); - append_list(list, (long long)name); - } - closedir(dir); - return list; -} - -long long ep_create_directory(long long path_ptr) { - const char* path = (const char*)path_ptr; - return mkdir(path, 0755) == 0 ? 1 : 0; -} - -long long ep_remove_file(long long path_ptr) { - const char* path = (const char*)path_ptr; - return remove(path) == 0 ? 1 : 0; -} - -long long ep_remove_directory(long long path_ptr) { - const char* path = (const char*)path_ptr; - return rmdir(path) == 0 ? 1 : 0; -} - -long long ep_rename_file(long long old_ptr, long long new_ptr) { - return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0; -} - -long long ep_copy_file(long long src_ptr, long long dst_ptr) { - const char* src = (const char*)src_ptr; - const char* dst = (const char*)dst_ptr; - FILE* fin = fopen(src, "rb"); - if (!fin) return 0; - FILE* fout = fopen(dst, "wb"); - if (!fout) { fclose(fin); return 0; } - char buf[8192]; - size_t n; - while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) { - fwrite(buf, 1, n, fout); - } - fclose(fin); - fclose(fout); - return 1; -} - -/* ========== Date/Time Runtime ========== */ -#include -#include - -long long ep_time_now_ms(void) { - struct timeval tv; - gettimeofday(&tv, NULL); - return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL; -} - -long long ep_time_now_sec(void) { - return (long long)time(NULL); -} - - -long long ep_time_year(long long ts) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - return tm ? tm->tm_year + 1900 : 0; -} - -long long ep_time_month(long long ts) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - return tm ? tm->tm_mon + 1 : 0; -} - -long long ep_time_day(long long ts) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - return tm ? tm->tm_mday : 0; -} - -long long ep_time_hour(long long ts) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - return tm ? tm->tm_hour : 0; -} - -long long ep_time_minute(long long ts) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - return tm ? tm->tm_min : 0; -} - -long long ep_time_second(long long ts) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - return tm ? tm->tm_sec : 0; -} - -long long ep_time_weekday(long long ts) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - return tm ? tm->tm_wday : 0; -} - -long long ep_format_time(long long ts, long long fmt_ptr) { - time_t t = (time_t)ts; - struct tm* tm = localtime(&t); - if (!tm) return (long long)""; - char* buf = (char*)malloc(256); - strftime(buf, 256, (const char*)fmt_ptr, tm); - return (long long)buf; -} - -/* ========== OS Runtime ========== */ - -long long ep_getenv(long long name_ptr) { - const char* val = getenv((const char*)name_ptr); - return val ? (long long)val : (long long)""; -} - -long long ep_setenv(long long name_ptr, long long val_ptr) { - return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0; -} - -long long ep_get_cwd(void) { - char* buf = (char*)malloc(4096); - if (getcwd(buf, 4096)) return (long long)buf; - free(buf); - return (long long)""; -} - -long long ep_os_name(void) { - #if defined(__APPLE__) - return (long long)"macos"; - #elif defined(__linux__) - return (long long)"linux"; - #elif defined(_WIN32) - return (long long)"windows"; - #else - return (long long)"unknown"; - #endif -} - -long long ep_arch_name(void) { - #if defined(__aarch64__) || defined(__arm64__) - return (long long)"arm64"; - #elif defined(__x86_64__) - return (long long)"x86_64"; - #elif defined(__i386__) - return (long long)"x86"; - #else - return (long long)"unknown"; - #endif -} - -long long ep_exit(long long code) { - exit((int)code); - return 0; -} - -long long ep_get_pid(void) { - return (long long)getpid(); -} - -long long ep_get_home_dir(void) { - const char* home = getenv("HOME"); - return home ? (long long)home : (long long)""; -} - -#ifdef __wasm__ -long long ep_run_command(long long cmd_ptr) { - (void)cmd_ptr; - return (long long)"Error: running external commands is not supported on WebAssembly"; -} -#else -long long ep_run_command(long long cmd_ptr) { - const char* cmd = (const char*)cmd_ptr; - FILE* fp = popen(cmd, "r"); - if (!fp) return (long long)""; - char* result = (char*)malloc(65536); - size_t total = 0; - char buf[4096]; - while (fgets(buf, sizeof(buf), fp)) { - size_t len = strlen(buf); - memcpy(result + total, buf, len); - total += len; - } - result[total] = '\0'; - pclose(fp); - return (long long)result; -} -#endif - -/* ========== HashMap helpers ========== */ - -long long ep_hash_string(long long s_ptr) { - const char* s = (const char*)s_ptr; - if (!s) return 0; - unsigned long long hash = 5381; - int c; - while ((c = *s++)) { - hash = ((hash << 5) + hash) + c; - } - return (long long)hash; -} - -long long ep_str_equals(long long a_ptr, long long b_ptr) { - if (a_ptr == b_ptr) return 1; - if (!a_ptr || !b_ptr) return 0; - /* If either value looks like a small integer (not a valid heap pointer), - fall back to integer comparison — strcmp would segfault. */ - if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0; - return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0; -} - -/* ========== Sync Primitives ========== */ - -#ifdef _WIN32 -long long ep_mutex_create(void) { - CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION)); - InitializeCriticalSection(m); - return (long long)m; -} -long long ep_mutex_lock_fn(long long m) { - EnterCriticalSection((CRITICAL_SECTION*)m); - return 1; -} -long long ep_mutex_unlock_fn(long long m) { - LeaveCriticalSection((CRITICAL_SECTION*)m); - return 1; -} -long long ep_mutex_trylock(long long m) { - return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0; -} -long long ep_mutex_destroy(long long m) { - DeleteCriticalSection((CRITICAL_SECTION*)m); - free((void*)m); - return 0; -} -#else -long long ep_mutex_create(void) { - pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(m, NULL); - return (long long)m; -} - -long long ep_mutex_lock_fn(long long m) { - return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0; -} - -long long ep_mutex_unlock_fn(long long m) { - return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0; -} - -long long ep_mutex_trylock(long long m) { - return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0; -} - -long long ep_mutex_destroy(long long m) { - pthread_mutex_destroy((pthread_mutex_t*)m); - free((void*)m); - return 0; -} -#endif - -#ifdef _WIN32 -long long ep_rwlock_create(void) { - SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK)); - InitializeSRWLock(rwl); - return (long long)rwl; -} -long long ep_rwlock_read_lock(long long rwl) { - AcquireSRWLockShared((SRWLOCK*)rwl); - return 1; -} -long long ep_rwlock_write_lock(long long rwl) { - AcquireSRWLockExclusive((SRWLOCK*)rwl); - return 1; -} -long long ep_rwlock_unlock(long long rwl) { - /* SRWLOCK does not have a single "unlock" — we try exclusive first. - In practice the caller should know which lock was taken. - ReleaseSRWLockExclusive on a shared lock is undefined, but - the runtime guarantees matched lock/unlock pairs. We default - to releasing the exclusive lock; shared unlock is handled - by pairing read_lock -> read_unlock if needed later. */ - ReleaseSRWLockExclusive((SRWLOCK*)rwl); - return 1; -} -long long ep_rwlock_destroy(long long rwl) { - /* SRWLOCK has no destroy */ - free((void*)rwl); - return 0; -} -#else -long long ep_rwlock_create(void) { - pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t)); - pthread_rwlock_init(rwl, NULL); - return (long long)rwl; -} - -long long ep_rwlock_read_lock(long long rwl) { - return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; -} - -long long ep_rwlock_write_lock(long long rwl) { - return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; -} - -long long ep_rwlock_unlock(long long rwl) { - return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; -} - -long long ep_rwlock_destroy(long long rwl) { - pthread_rwlock_destroy((pthread_rwlock_t*)rwl); - free((void*)rwl); - return 0; -} -#endif - -#ifdef _MSC_VER -long long ep_atomic_create(long long initial) { - volatile long long* a = (volatile long long*)malloc(sizeof(long long)); - InterlockedExchange64(a, initial); - return (long long)a; -} -long long ep_atomic_load(long long a) { - return InterlockedCompareExchange64((volatile long long*)a, 0, 0); -} -long long ep_atomic_store(long long a, long long value) { - InterlockedExchange64((volatile long long*)a, value); - return value; -} -long long ep_atomic_add(long long a, long long delta) { - return InterlockedExchangeAdd64((volatile long long*)a, delta); -} -long long ep_atomic_sub(long long a, long long delta) { - return InterlockedExchangeAdd64((volatile long long*)a, -delta); -} -long long ep_atomic_cas(long long a, long long expected, long long desired) { - long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected); - return (old == expected) ? 1 : 0; -} -#else -long long ep_atomic_create(long long initial) { - long long* a = (long long*)malloc(sizeof(long long)); - __atomic_store_n(a, initial, __ATOMIC_SEQ_CST); - return (long long)a; -} - -long long ep_atomic_load(long long a) { - return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST); -} - -long long ep_atomic_store(long long a, long long value) { - __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST); - return value; -} - -long long ep_atomic_add(long long a, long long delta) { - return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST); -} - -long long ep_atomic_sub(long long a, long long delta) { - return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST); -} - -long long ep_atomic_cas(long long a, long long expected, long long desired) { - long long exp = expected; - return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0; -} -#endif - -/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */ -typedef struct { - pthread_mutex_t mutex; - pthread_cond_t cond; - unsigned count; - unsigned target; - unsigned generation; -} EpBarrier; - -long long ep_barrier_create(long long count) { - EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier)); - pthread_mutex_init(&b->mutex, NULL); - pthread_cond_init(&b->cond, NULL); - b->count = 0; - b->target = (unsigned)count; - b->generation = 0; - return (long long)b; -} - -long long ep_barrier_wait(long long bp) { - EpBarrier* b = (EpBarrier*)bp; - pthread_mutex_lock(&b->mutex); - unsigned gen = b->generation; - b->count++; - if (b->count >= b->target) { - b->count = 0; - b->generation++; - pthread_cond_broadcast(&b->cond); - pthread_mutex_unlock(&b->mutex); - return 1; /* serial thread */ - } - while (gen == b->generation) { - pthread_cond_wait(&b->cond, &b->mutex); - } - pthread_mutex_unlock(&b->mutex); - return 0; -} - -long long ep_barrier_destroy(long long bp) { - EpBarrier* b = (EpBarrier*)bp; - pthread_mutex_destroy(&b->mutex); - pthread_cond_destroy(&b->cond); - free(b); - return 0; -} - -/* Semaphore via mutex+condvar (portable) */ -typedef struct { - pthread_mutex_t mutex; - pthread_cond_t cond; - long long value; -} EpSemaphore; - -long long ep_semaphore_create(long long initial) { - EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore)); - pthread_mutex_init(&s->mutex, NULL); - pthread_cond_init(&s->cond, NULL); - s->value = initial; - return (long long)s; -} - -long long ep_semaphore_wait(long long sp) { - EpSemaphore* s = (EpSemaphore*)sp; - pthread_mutex_lock(&s->mutex); - while (s->value <= 0) { - pthread_cond_wait(&s->cond, &s->mutex); - } - s->value--; - pthread_mutex_unlock(&s->mutex); - return 1; -} - -long long ep_semaphore_post(long long sp) { - EpSemaphore* s = (EpSemaphore*)sp; - pthread_mutex_lock(&s->mutex); - s->value++; - pthread_cond_signal(&s->cond); - pthread_mutex_unlock(&s->mutex); - return 1; -} - -long long ep_semaphore_trywait(long long sp) { - EpSemaphore* s = (EpSemaphore*)sp; - pthread_mutex_lock(&s->mutex); - if (s->value > 0) { - s->value--; - pthread_mutex_unlock(&s->mutex); - return 1; - } - pthread_mutex_unlock(&s->mutex); - return 0; -} - -long long ep_semaphore_destroy(long long sp) { - EpSemaphore* s = (EpSemaphore*)sp; - pthread_mutex_destroy(&s->mutex); - pthread_cond_destroy(&s->cond); - free(s); - return 0; -} - -long long ep_condvar_create(void) { - pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t)); - pthread_cond_init(cv, NULL); - return (long long)cv; -} - -long long ep_condvar_wait(long long cv, long long m) { - return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0; -} - -long long ep_condvar_signal(long long cv) { - return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0; -} - -long long ep_condvar_broadcast(long long cv) { - return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0; -} - -long long ep_condvar_destroy(long long cv) { - pthread_cond_destroy((pthread_cond_t*)cv); - free((void*)cv); - return 0; -} - -/* ========== Regex (simple stub — delegates to POSIX regex) ========== */ -#include - -long long ep_regex_match(long long pattern_ptr, long long text_ptr) { - regex_t regex; - const char* pattern = (const char*)pattern_ptr; - const char* text = (const char*)text_ptr; - int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB); - if (ret) return 0; - ret = regexec(®ex, text, 0, NULL, 0); - regfree(®ex); - return ret == 0 ? 1 : 0; -} - -long long ep_regex_find(long long pattern_ptr, long long text_ptr) { - regex_t regex; - regmatch_t match; - const char* pattern = (const char*)pattern_ptr; - const char* text = (const char*)text_ptr; - int ret = regcomp(®ex, pattern, REG_EXTENDED); - if (ret) return (long long)""; - ret = regexec(®ex, text, 1, &match, 0); - if (ret != 0) { regfree(®ex); return (long long)""; } - int len = match.rm_eo - match.rm_so; - char* result = (char*)malloc(len + 1); - memcpy(result, text + match.rm_so, len); - result[len] = '\0'; - regfree(®ex); - return (long long)result; -} - -long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) { - regex_t regex; - regmatch_t match; - const char* pattern = (const char*)pattern_ptr; - const char* text = (const char*)text_ptr; - long long list = create_list(); - int ret = regcomp(®ex, pattern, REG_EXTENDED); - if (ret) return list; - const char* cursor = text; - while (regexec(®ex, cursor, 1, &match, 0) == 0) { - int len = match.rm_eo - match.rm_so; - char* result = (char*)malloc(len + 1); - memcpy(result, cursor + match.rm_so, len); - result[len] = '\0'; - append_list(list, (long long)result); - cursor += match.rm_eo; - if (match.rm_eo == 0) break; - } - regfree(®ex); - return list; -} - -long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) { - /* Simple single-replacement via regex */ - regex_t regex; - regmatch_t match; - const char* pattern = (const char*)pattern_ptr; - const char* text = (const char*)text_ptr; - const char* repl = (const char*)repl_ptr; - int ret = regcomp(®ex, pattern, REG_EXTENDED); - if (ret) return text_ptr; - ret = regexec(®ex, text, 1, &match, 0); - if (ret != 0) { regfree(®ex); return text_ptr; } - size_t tlen = strlen(text); - size_t rlen = strlen(repl); - size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen; - char* result = (char*)malloc(new_len + 1); - memcpy(result, text, match.rm_so); - memcpy(result + match.rm_so, repl, rlen); - memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo); - result[new_len] = '\0'; - regfree(®ex); - return (long long)result; -} - -long long ep_regex_split(long long pattern_ptr, long long text_ptr) { - long long list = create_list(); - /* Simple split: find matches and split around them */ - regex_t regex; - regmatch_t match; - const char* pattern = (const char*)pattern_ptr; - const char* text = (const char*)text_ptr; - int ret = regcomp(®ex, pattern, REG_EXTENDED); - if (ret) { - append_list(list, text_ptr); - return list; - } - const char* cursor = text; - while (regexec(®ex, cursor, 1, &match, 0) == 0) { - int len = match.rm_so; - char* part = (char*)malloc(len + 1); - memcpy(part, cursor, len); - part[len] = '\0'; - append_list(list, (long long)part); - cursor += match.rm_eo; - if (match.rm_eo == 0) break; - } - char* rest = strdup(cursor); - append_list(list, (long long)rest); - regfree(®ex); - return list; -} - -/* ========== Base64 ========== */ -static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -long long ep_base64_encode(long long data_ptr) { - const unsigned char* data = (const unsigned char*)data_ptr; - size_t len = strlen((const char*)data); - size_t out_len = 4 * ((len + 2) / 3); - char* out = (char*)malloc(out_len + 1); - size_t i, j = 0; - for (i = 0; i < len; i += 3) { - unsigned int n = data[i] << 16; - if (i + 1 < len) n |= data[i+1] << 8; - if (i + 2 < len) n |= data[i+2]; - out[j++] = b64_table[(n >> 18) & 63]; - out[j++] = b64_table[(n >> 12) & 63]; - out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '='; - out[j++] = (i + 2 < len) ? b64_table[n & 63] : '='; - } - out[j] = '\0'; - return (long long)out; -} - -long long ep_uuid_v4(void) { - char* uuid = (char*)malloc(37); - unsigned char bytes[16]; - ep_secure_random_bytes(bytes, 16); - bytes[6] = (bytes[6] & 0x0F) | 0x40; - bytes[8] = (bytes[8] & 0x3F) | 0x80; - snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - bytes[0], bytes[1], bytes[2], bytes[3], - bytes[4], bytes[5], bytes[6], bytes[7], - bytes[8], bytes[9], bytes[10], bytes[11], - bytes[12], bytes[13], bytes[14], bytes[15]); - return (long long)uuid; -} - -long long file_read(long long path_val) { - const char* path = (const char*)path_val; - if (!path) return (long long)strdup(""); - FILE* f = fopen(path, "rb"); - if (!f) return (long long)strdup(""); - fseek(f, 0, SEEK_END); - long size = ftell(f); - fseek(f, 0, SEEK_SET); - char* buf = malloc(size + 1); - if (!buf) { fclose(f); return (long long)strdup(""); } - fread(buf, 1, size, f); - buf[size] = '\0'; - fclose(f); - ep_gc_register(buf, EP_OBJ_STRING); - return (long long)buf; -} - -long long file_write(long long path_val, long long content_val) { - const char* path = (const char*)path_val; - const char* content = (const char*)content_val; - if (!path || !content) return 0; - FILE* f = fopen(path, "wb"); - if (!f) return 0; - size_t len = strlen(content); - fwrite(content, 1, len, f); - fclose(f); - return 1; -} - -long long file_append(long long path_val, long long content_val) { - const char* path = (const char*)path_val; - const char* content = (const char*)content_val; - if (!path || !content) return 0; - FILE* f = fopen(path, "ab"); - if (!f) return 0; - size_t len = strlen(content); - fwrite(content, 1, len, f); - fclose(f); - return 1; -} - -long long file_exists(long long path_val) { - const char* path = (const char*)path_val; - if (!path) return 0; - FILE* f = fopen(path, "r"); - if (f) { fclose(f); return 1; } - return 0; -} - -long long string_contains(long long s_val, long long sub_val) { - const char* s = (const char*)s_val; - const char* sub = (const char*)sub_val; - if (!s || !sub) return 0; - return strstr(s, sub) != NULL ? 1 : 0; -} - -long long string_index_of(long long s_val, long long sub_val) { - const char* s = (const char*)s_val; - const char* sub = (const char*)sub_val; - if (!s || !sub) return -1; - const char* found = strstr(s, sub); - if (!found) return -1; - return (long long)(found - s); -} - -long long string_replace(long long s_val, long long old_val, long long new_val) { - const char* s = (const char*)s_val; - const char* old_str = (const char*)old_val; - const char* new_str = (const char*)new_val; - if (!s || !old_str || !new_str) return (long long)strdup(s ? s : ""); - size_t old_len = strlen(old_str); - size_t new_len = strlen(new_str); - if (old_len == 0) return (long long)strdup(s); - int count = 0; - const char* p = s; - while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; } - size_t result_len = strlen(s) + count * (new_len - old_len); - char* result = malloc(result_len + 1); - if (!result) return (long long)strdup(s); - char* dst = result; - p = s; - while (*p) { - if (strncmp(p, old_str, old_len) == 0) { - memcpy(dst, new_str, new_len); - dst += new_len; - p += old_len; - } else { - *dst++ = *p++; - } - } - *dst = '\0'; - ep_gc_register(result, EP_OBJ_STRING); - return (long long)result; -} - -/* ========== Additional String Functions ========== */ -#include - -long long string_upper(long long s_val) { - const char* s = (const char*)s_val; - if (!s) return (long long)strdup(""); - long long len = strlen(s); - char* result = malloc(len + 1); - for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]); - result[len] = '\0'; - ep_gc_register(result, EP_OBJ_STRING); - return (long long)result; -} - -long long string_lower(long long s_val) { - const char* s = (const char*)s_val; - if (!s) return (long long)strdup(""); - long long len = strlen(s); - char* result = malloc(len + 1); - for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]); - result[len] = '\0'; - ep_gc_register(result, EP_OBJ_STRING); - return (long long)result; -} - -long long string_trim(long long s_val) { - const char* s = (const char*)s_val; - if (!s) return (long long)strdup(""); - while (*s && isspace((unsigned char)*s)) s++; - long long len = strlen(s); - while (len > 0 && isspace((unsigned char)s[len - 1])) len--; - char* result = malloc(len + 1); - memcpy(result, s, len); - result[len] = '\0'; - ep_gc_register(result, EP_OBJ_STRING); - return (long long)result; -} - -long long string_split(long long s_val, long long delim_val) { - const char* s = (const char*)s_val; - const char* delim = (const char*)delim_val; - if (!s || !delim) return create_list(); - long long list = create_list(); - long long dlen = strlen(delim); - if (dlen == 0) { append_list(list, s_val); return list; } - const char* p = s; - while (1) { - const char* found = strstr(p, delim); - long long partlen = found ? (found - p) : (long long)strlen(p); - char* part = malloc(partlen + 1); - memcpy(part, p, partlen); - part[partlen] = '\0'; - ep_gc_register(part, EP_OBJ_STRING); - append_list(list, (long long)part); - if (!found) break; - p = found + dlen; - } - return list; -} - -long long char_at(long long s_val, long long index) { - const char* s = (const char*)s_val; - if (!s || index < 0 || index >= (long long)strlen(s)) return 0; - return (unsigned char)s[index]; -} - -long long char_from_code(long long code) { - char* result = malloc(2); - result[0] = (char)code; - result[1] = '\0'; - ep_gc_register(result, EP_OBJ_STRING); - return (long long)result; -} - -long long ep_abs(long long n) { - return n < 0 ? -n : n; -} - -// Auto-convert any value to string for string interpolation -long long ep_auto_to_string(long long val) { - // If the value is 0, return "0" - if (val == 0) return (long long)strdup("0"); - // Check if val is a GC-tracked string (heap-allocated) - EpGCObject* obj = ep_gc_find((void*)val); - if (obj && obj->kind == EP_OBJ_STRING) { - return val; // It's a known string pointer - } - // Check if val is a static string literal (in .rodata/.data segment) - // These aren't GC-tracked but ARE valid pointers. Use a safe probe: - // only dereference if the address is in a readable memory page. - if (val > 0x100000) { -#if defined(_WIN32) - // Windows: use VirtualQuery to safely probe pointer validity - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) { - const char* p = (const char*)(void*)val; - unsigned char first = (unsigned char)*p; - if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { - return val; // Readable memory, looks like a string - } - } -#elif defined(__APPLE__) - // macOS: use vm_read_overwrite to safely probe - char probe; - vm_size_t sz = 1; - kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz); - if (kr == KERN_SUCCESS) { - unsigned char first = (unsigned char)probe; - if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { - return val; // Readable memory, looks like a string - } - } -#else - // Linux: use write() to /dev/null as a safe pointer probe - // write() returns -1 with EFAULT for invalid pointers, no signal - int devnull = open("/dev/null", 1); // O_WRONLY - if (devnull >= 0) { - ssize_t r = write(devnull, (const void*)val, 1); - close(devnull); - if (r == 1) { - const char* p = (const char*)(void*)val; - unsigned char first = (unsigned char)*p; - if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { - return val; - } - } - } -#endif - } - // Otherwise, convert integer to string - char* buf = (char*)malloc(32); - snprintf(buf, 32, "%lld", val); - ep_gc_register(buf, EP_OBJ_STRING); - return (long long)buf; -} - -long long ep_random_int(long long min, long long max) { - if (max <= min) return min; - /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */ - unsigned long long range = (unsigned long long)(max - min) + 1ULL; - unsigned long long limit = UINT64_MAX - (UINT64_MAX % range); - unsigned long long r; - do { - ep_secure_random_bytes((unsigned char*)&r, sizeof(r)); - } while (r >= limit); - return min + (long long)(r % range); -} - -// JSON built-in functions -static const char* json_skip_ws(const char* p) { - while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; - return p; -} - -static const char* json_skip_value(const char* p) { - p = json_skip_ws(p); - if (*p == '"') { - p++; - while (*p && *p != '"') { if (*p == '\\') p++; p++; } - if (*p == '"') p++; - } else if (*p == '{') { - int depth = 1; p++; - while (*p && depth > 0) { - if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; } - else if (*p == '{') { depth++; p++; } - else if (*p == '}') { depth--; p++; } - else p++; - } - } else if (*p == '[') { - int depth = 1; p++; - while (*p && depth > 0) { - if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; } - else if (*p == '[') { depth++; p++; } - else if (*p == ']') { depth--; p++; } - else p++; - } - } else { - while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\n') p++; - } - return p; -} - -static const char* json_find_key(const char* json, const char* key) { - const char* p = json_skip_ws(json); - if (*p != '{') return NULL; - p++; - while (*p) { - p = json_skip_ws(p); - if (*p == '}') return NULL; - if (*p != '"') return NULL; - p++; - const char* ks = p; - while (*p && *p != '"') { if (*p == '\\') p++; p++; } - size_t klen = p - ks; - if (*p == '"') p++; - p = json_skip_ws(p); - if (*p == ':') p++; - p = json_skip_ws(p); - if (klen == strlen(key) && strncmp(ks, key, klen) == 0) { - return p; - } - p = json_skip_value(p); - p = json_skip_ws(p); - if (*p == ',') p++; - } - return NULL; -} - -long long json_get_string(long long json_val, long long key_val) { - const char* json = (const char*)json_val; - const char* key = (const char*)key_val; - if (!json || !key) return (long long)strdup(""); - const char* val = json_find_key(json, key); - if (!val || *val != '"') return (long long)strdup(""); - val++; - const char* end = val; - while (*end && *end != '"') { if (*end == '\\') end++; end++; } - size_t len = end - val; - char* result = (char*)malloc(len + 1); - // Handle escape sequences - size_t di = 0; - const char* si = val; - while (si < end) { - if (*si == '\\' && si + 1 < end) { - si++; - switch (*si) { - case 'n': result[di++] = '\n'; break; - case 't': result[di++] = '\t'; break; - case 'r': result[di++] = '\r'; break; - case '"': result[di++] = '"'; break; - case '\\': result[di++] = '\\'; break; - default: result[di++] = *si; break; - } - } else { - result[di++] = *si; - } - si++; - } - result[di] = '\0'; - ep_gc_register(result, EP_OBJ_STRING); - return (long long)result; -} - -long long json_get_int(long long json_val, long long key_val) { - const char* json = (const char*)json_val; - const char* key = (const char*)key_val; - if (!json || !key) return 0; - const char* val = json_find_key(json, key); - if (!val) return 0; - return atoll(val); -} - -long long json_get_bool(long long json_val, long long key_val) { - const char* json = (const char*)json_val; - const char* key = (const char*)key_val; - if (!json || !key) return 0; - const char* val = json_find_key(json, key); - if (!val) return 0; - if (strncmp(val, "true", 4) == 0) return 1; - return 0; -} - -// SHA-1 implementation (RFC 3174) for WebSocket handshake -static unsigned int sha1_left_rotate(unsigned int x, int n) { - return (x << n) | (x >> (32 - n)); -} - -long long ep_sha1(long long data_val) { - const unsigned char* data = (const unsigned char*)data_val; - if (!data) return (long long)strdup(""); - size_t len = strlen((const char*)data); - - unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0; - size_t new_len = len + 1; - while (new_len % 64 != 56) new_len++; - unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1); - memcpy(msg, data, len); - msg[len] = 0x80; - unsigned long long bits_len = (unsigned long long)len * 8; - for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8)); - - for (size_t offset = 0; offset < new_len + 8; offset += 64) { - unsigned int w[80]; - for (int i = 0; i < 16; i++) { - w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) | - ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3]; - } - for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1); - unsigned int a = h0, b = h1, c = h2, d = h3, e = h4; - for (int i = 0; i < 80; i++) { - unsigned int f, k; - if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; } - else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; } - else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; } - else { f = b ^ c ^ d; k = 0xCA62C1D6; } - unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i]; - e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp; - } - h0 += a; h1 += b; h2 += c; h3 += d; h4 += e; - } - free(msg); - - // Return Base64-encoded hash directly (for WebSocket handshake) - unsigned char hash[20]; - hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF; - hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF; - hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF; - hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF; - hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF; - - // Base64 encode the 20-byte hash - size_t b64_len = 4 * ((20 + 2) / 3); - char* result = (char*)malloc(b64_len + 1); - size_t j = 0; - for (size_t bi = 0; bi < 20; bi += 3) { - unsigned int n2 = ((unsigned int)hash[bi]) << 16; - if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8; - if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2]; - result[j++] = b64_table[(n2 >> 18) & 0x3F]; - result[j++] = b64_table[(n2 >> 12) & 0x3F]; - result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '='; - result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '='; - } - result[j] = '\0'; - ep_gc_register(result, EP_OBJ_STRING); - return (long long)result; -} - -// Read exact N bytes from a socket -#ifdef __wasm__ -long long ep_net_recv_bytes(long long fd, long long count) { - (void)fd; (void)count; - return (long long)strdup(""); -} -#else -long long ep_net_recv_bytes(long long fd, long long count) { - if (count <= 0) return (long long)strdup(""); - char* buf = (char*)malloc(count + 1); -#ifdef _WIN32 - int total = 0; - while (total < (int)count) { - int n = recv((int)fd, buf + total, (int)(count - total), 0); - if (n <= 0) break; - total += n; - } -#else - ssize_t total = 0; - while (total < count) { - ssize_t n = recv((int)fd, buf + total, count - total, 0); - if (n <= 0) break; - total += n; - } -#endif - buf[total] = '\0'; - ep_gc_register(buf, EP_OBJ_STRING); - return (long long)buf; -} -#endif - -long long ep_get_args(void) { - long long list_ptr = create_list(); - for (int i = 0; i < ep_argc; i++) { - char* arg_copy = strdup(ep_argv[i]); - ep_gc_register(arg_copy, EP_OBJ_STRING); - append_list(list_ptr, (long long)arg_copy); - } - return list_ptr; -} - -"#; +const RUNTIME_HEADER_AND_SRC: &str = include_str!("../runtime/ep_runtime.c"); #[allow(dead_code)] const C_MAIN_BOOTSTRAPPER: &str = r#" From e3f2b20a22b56981d84b1ea6017c9f88cacfc5cd Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:00:07 +0100 Subject: [PATCH 03/51] refactor: extract static builtin layer to runtime/ep_builtins.c (byte-identical) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit concat, int_to_string, str/ptr helpers, StringBuilder, read_line/read_int, GC counters, and float conversions move from inline push_str calls to runtime/ep_builtins.c embedded via include_str!. Emitted C verified hash-identical before/after (70c4909c…). Second of the two shared-runtime files the self-hosted compiler will embed. run_tests.sh 69/69; self-hosting gate passes. Co-Authored-By: Claude Opus 4.8 (1M context) --- runtime/ep_builtins.c | 161 ++++++++++++++++++++++++++++++++++++++++ src/codegen.rs | 169 +----------------------------------------- 2 files changed, 165 insertions(+), 165 deletions(-) create mode 100644 runtime/ep_builtins.c diff --git a/runtime/ep_builtins.c b/runtime/ep_builtins.c new file mode 100644 index 0000000..af604ec --- /dev/null +++ b/runtime/ep_builtins.c @@ -0,0 +1,161 @@ + +/* Built-in: string concatenation */ +long long concat(long long a, long long b) { + const char* sa = (const char*)a; + const char* sb = (const char*)b; + long long la = strlen(sa); + long long lb = strlen(sb); + char* result = malloc(la + lb + 1); + memcpy(result, sa, la); + memcpy(result + la, sb, lb); + result[la + lb] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long int_to_string(long long val) { + char* buf = malloc(32); + snprintf(buf, 32, "%lld", val); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + +long long ep_int_to_str(long long val) { return int_to_string(val); } + +typedef struct { char* data; long long len; long long cap; } EpStringBuilder; + +long long ep_sb_create(long long dummy) { + (void)dummy; + EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder)); + sb->cap = 256; + sb->len = 0; + sb->data = (char*)malloc(sb->cap); + sb->data[0] = '\0'; + return (long long)sb; +} + +long long ep_sb_append(long long sb_ptr, long long str_ptr) { + EpStringBuilder* sb = (EpStringBuilder*)sb_ptr; + const char* s = (const char*)str_ptr; + if (!s) return sb_ptr; + long long slen = strlen(s); + while (sb->len + slen + 1 > sb->cap) { + sb->cap *= 2; + sb->data = (char*)realloc(sb->data, sb->cap); + } + memcpy(sb->data + sb->len, s, slen); + sb->len += slen; + sb->data[sb->len] = '\0'; + return sb_ptr; +} + +long long ep_sb_append_int(long long sb_ptr, long long val) { + char buf[32]; + snprintf(buf, sizeof(buf), "%lld", val); + return ep_sb_append(sb_ptr, (long long)buf); +} + +long long ep_sb_to_string(long long sb_ptr) { + EpStringBuilder* sb = (EpStringBuilder*)sb_ptr; + char* result = (char*)malloc(sb->len + 1); + memcpy(result, sb->data, sb->len + 1); + ep_gc_register(result, EP_OBJ_STRING); + free(sb->data); + free(sb); + return (long long)result; +} + +long long ep_sb_length(long long sb_ptr) { + return ((EpStringBuilder*)sb_ptr)->len; +} + +long long str_to_ptr(long long s) { return s; } +long long ptr_to_str(long long p) { + if (p == 0) return (long long)strdup(""); + char* copy = strdup((const char*)p); + ep_gc_register(copy, EP_OBJ_STRING); + return (long long)copy; +} + +long long peek_byte(long long ptr, long long offset) { + return (long long)((unsigned char*)ptr)[offset]; +} +long long poke_byte(long long ptr, long long offset, long long value) { + ((unsigned char*)ptr)[offset] = (unsigned char)value; + return 0; +} +long long alloc_bytes(long long size) { + return (long long)calloc((size_t)size, 1); +} +long long free_bytes(long long ptr) { + free((void*)ptr); + return 0; +} +long long list_to_bytes(long long list_ptr) { + long long len = length_list(list_ptr); + unsigned char* buf = (unsigned char*)malloc(len); + for (long long i = 0; i < len; i++) { + buf[i] = (unsigned char)get_list(list_ptr, i); + } + return (long long)buf; +} +long long bytes_to_list(long long ptr, long long len) { + long long list = create_list(); + unsigned char* buf = (unsigned char*)ptr; + for (long long i = 0; i < len; i++) { + append_list(list, (long long)buf[i]); + } + return list; +} + +long long ep_gc_get_minor_count() { + return ep_gc_minor_count; +} +long long ep_gc_get_major_count() { + return ep_gc_major_count; +} +long long ep_gc_get_nursery_count() { + return ep_gc_nursery_count; +} + +long long string_to_int(long long s) { + if (s == 0) return 0; + return atoll((const char*)s); +} + +long long read_line() { + char buf[4096]; + if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\0'; } + size_t len = strlen(buf); + if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0'; + char* result = strdup(buf); + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long read_int() { + long long val = 0; + scanf("%lld", &val); + while(getchar() != '\n'); + return val; +} + +long long read_float() { + double val = 0.0; + scanf("%lf", &val); + while(getchar() != '\n'); + long long result; memcpy(&result, &val, sizeof(double)); + return result; +} + +long long int_to_float(long long val) { + double d = (double)val; + long long result; memcpy(&result, &d, sizeof(double)); + return result; +} + +long long float_to_int(long long val) { + double d; memcpy(&d, &val, sizeof(double)); + return (long long)d; +} + diff --git a/src/codegen.rs b/src/codegen.rs index 60051db..1f9b650 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -3066,171 +3066,9 @@ impl Codegen { } } - // Emit concat built-in - self.out.push_str("\n/* Built-in: string concatenation */\n"); - self.out.push_str("long long concat(long long a, long long b) {\n"); - self.out.push_str(" const char* sa = (const char*)a;\n"); - self.out.push_str(" const char* sb = (const char*)b;\n"); - self.out.push_str(" long long la = strlen(sa);\n"); - self.out.push_str(" long long lb = strlen(sb);\n"); - self.out.push_str(" char* result = malloc(la + lb + 1);\n"); - self.out.push_str(" memcpy(result, sa, la);\n"); - self.out.push_str(" memcpy(result + la, sb, lb);\n"); - self.out.push_str(" result[la + lb] = '\\0';\n"); - self.out.push_str(" ep_gc_register(result, EP_OBJ_STRING);\n"); - self.out.push_str(" return (long long)result;\n"); - self.out.push_str("}\n\n"); - - self.out.push_str("long long int_to_string(long long val) {\n"); - self.out.push_str(" char* buf = malloc(32);\n"); - self.out.push_str(" snprintf(buf, 32, \"%lld\", val);\n"); - self.out.push_str(" ep_gc_register(buf, EP_OBJ_STRING);\n"); - self.out.push_str(" return (long long)buf;\n"); - self.out.push_str("}\n\n"); - - // ep_int_to_str alias - self.out.push_str("long long ep_int_to_str(long long val) { return int_to_string(val); }\n\n"); - - // Native String Builder — realloc-based for O(n) amortized appends - self.out.push_str("typedef struct { char* data; long long len; long long cap; } EpStringBuilder;\n\n"); - self.out.push_str("long long ep_sb_create(long long dummy) {\n"); - self.out.push_str(" (void)dummy;\n"); - self.out.push_str(" EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));\n"); - self.out.push_str(" sb->cap = 256;\n"); - self.out.push_str(" sb->len = 0;\n"); - self.out.push_str(" sb->data = (char*)malloc(sb->cap);\n"); - self.out.push_str(" sb->data[0] = '\\0';\n"); - self.out.push_str(" return (long long)sb;\n"); - self.out.push_str("}\n\n"); - - self.out.push_str("long long ep_sb_append(long long sb_ptr, long long str_ptr) {\n"); - self.out.push_str(" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n"); - self.out.push_str(" const char* s = (const char*)str_ptr;\n"); - self.out.push_str(" if (!s) return sb_ptr;\n"); - self.out.push_str(" long long slen = strlen(s);\n"); - self.out.push_str(" while (sb->len + slen + 1 > sb->cap) {\n"); - self.out.push_str(" sb->cap *= 2;\n"); - self.out.push_str(" sb->data = (char*)realloc(sb->data, sb->cap);\n"); - self.out.push_str(" }\n"); - self.out.push_str(" memcpy(sb->data + sb->len, s, slen);\n"); - self.out.push_str(" sb->len += slen;\n"); - self.out.push_str(" sb->data[sb->len] = '\\0';\n"); - self.out.push_str(" return sb_ptr;\n"); - self.out.push_str("}\n\n"); - - self.out.push_str("long long ep_sb_append_int(long long sb_ptr, long long val) {\n"); - self.out.push_str(" char buf[32];\n"); - self.out.push_str(" snprintf(buf, sizeof(buf), \"%lld\", val);\n"); - self.out.push_str(" return ep_sb_append(sb_ptr, (long long)buf);\n"); - self.out.push_str("}\n\n"); - - self.out.push_str("long long ep_sb_to_string(long long sb_ptr) {\n"); - self.out.push_str(" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n"); - self.out.push_str(" char* result = (char*)malloc(sb->len + 1);\n"); - self.out.push_str(" memcpy(result, sb->data, sb->len + 1);\n"); - self.out.push_str(" ep_gc_register(result, EP_OBJ_STRING);\n"); - self.out.push_str(" free(sb->data);\n"); - self.out.push_str(" free(sb);\n"); - self.out.push_str(" return (long long)result;\n"); - self.out.push_str("}\n\n"); - - self.out.push_str("long long ep_sb_length(long long sb_ptr) {\n"); - self.out.push_str(" return ((EpStringBuilder*)sb_ptr)->len;\n"); - self.out.push_str("}\n\n"); - - // FFI pointer/byte builtins - self.out.push_str("long long str_to_ptr(long long s) { return s; }\n"); - self.out.push_str("long long ptr_to_str(long long p) {\n"); - self.out.push_str(" if (p == 0) return (long long)strdup(\"\");\n"); - self.out.push_str(" char* copy = strdup((const char*)p);\n"); - self.out.push_str(" ep_gc_register(copy, EP_OBJ_STRING);\n"); - self.out.push_str(" return (long long)copy;\n"); - self.out.push_str("}\n\n"); - self.out.push_str("long long peek_byte(long long ptr, long long offset) {\n"); - self.out.push_str(" return (long long)((unsigned char*)ptr)[offset];\n"); - self.out.push_str("}\n"); - self.out.push_str("long long poke_byte(long long ptr, long long offset, long long value) {\n"); - self.out.push_str(" ((unsigned char*)ptr)[offset] = (unsigned char)value;\n"); - self.out.push_str(" return 0;\n"); - self.out.push_str("}\n"); - self.out.push_str("long long alloc_bytes(long long size) {\n"); - self.out.push_str(" return (long long)calloc((size_t)size, 1);\n"); - self.out.push_str("}\n"); - self.out.push_str("long long free_bytes(long long ptr) {\n"); - self.out.push_str(" free((void*)ptr);\n"); - self.out.push_str(" return 0;\n"); - self.out.push_str("}\n"); - self.out.push_str("long long list_to_bytes(long long list_ptr) {\n"); - self.out.push_str(" long long len = length_list(list_ptr);\n"); - self.out.push_str(" unsigned char* buf = (unsigned char*)malloc(len);\n"); - self.out.push_str(" for (long long i = 0; i < len; i++) {\n"); - self.out.push_str(" buf[i] = (unsigned char)get_list(list_ptr, i);\n"); - self.out.push_str(" }\n"); - self.out.push_str(" return (long long)buf;\n"); - self.out.push_str("}\n"); - self.out.push_str("long long bytes_to_list(long long ptr, long long len) {\n"); - self.out.push_str(" long long list = create_list();\n"); - self.out.push_str(" unsigned char* buf = (unsigned char*)ptr;\n"); - self.out.push_str(" for (long long i = 0; i < len; i++) {\n"); - self.out.push_str(" append_list(list, (long long)buf[i]);\n"); - self.out.push_str(" }\n"); - self.out.push_str(" return list;\n"); - self.out.push_str("}\n\n"); - self.out.push_str("long long ep_gc_get_minor_count() {\n"); - self.out.push_str(" return ep_gc_minor_count;\n"); - self.out.push_str("}\n"); - self.out.push_str("long long ep_gc_get_major_count() {\n"); - self.out.push_str(" return ep_gc_major_count;\n"); - self.out.push_str("}\n"); - self.out.push_str("long long ep_gc_get_nursery_count() {\n"); - self.out.push_str(" return ep_gc_nursery_count;\n"); - self.out.push_str("}\n\n"); - - self.out.push_str("long long string_to_int(long long s) {\n"); - self.out.push_str(" if (s == 0) return 0;\n"); - self.out.push_str(" return atoll((const char*)s);\n"); - self.out.push_str("}\n\n"); - - // read_line: reads a line from stdin, returns dynamically allocated string - self.out.push_str("long long read_line() {\n"); - self.out.push_str(" char buf[4096];\n"); - self.out.push_str(" if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\\0'; }\n"); - self.out.push_str(" size_t len = strlen(buf);\n"); - self.out.push_str(" if (len > 0 && buf[len-1] == '\\n') buf[len-1] = '\\0';\n"); - self.out.push_str(" char* result = strdup(buf);\n"); - self.out.push_str(" ep_gc_register(result, EP_OBJ_STRING);\n"); - self.out.push_str(" return (long long)result;\n"); - self.out.push_str("}\n\n"); - - // read_int: reads an integer from stdin - self.out.push_str("long long read_int() {\n"); - self.out.push_str(" long long val = 0;\n"); - self.out.push_str(" scanf(\"%lld\", &val);\n"); - self.out.push_str(" while(getchar() != '\\n');\n"); - self.out.push_str(" return val;\n"); - self.out.push_str("}\n\n"); - - // read_float: reads a float from stdin, returns as type-punned long long - self.out.push_str("long long read_float() {\n"); - self.out.push_str(" double val = 0.0;\n"); - self.out.push_str(" scanf(\"%lf\", &val);\n"); - self.out.push_str(" while(getchar() != '\\n');\n"); - self.out.push_str(" long long result; memcpy(&result, &val, sizeof(double));\n"); - self.out.push_str(" return result;\n"); - self.out.push_str("}\n\n"); - - // int_to_float: converts int to float (type-punned as long long) - self.out.push_str("long long int_to_float(long long val) {\n"); - self.out.push_str(" double d = (double)val;\n"); - self.out.push_str(" long long result; memcpy(&result, &d, sizeof(double));\n"); - self.out.push_str(" return result;\n"); - self.out.push_str("}\n\n"); - - // float_to_int: converts float (type-punned long long) back to int - self.out.push_str("long long float_to_int(long long val) {\n"); - self.out.push_str(" double d; memcpy(&d, &val, sizeof(double));\n"); - self.out.push_str(" return (long long)d;\n"); - self.out.push_str("}\n\n"); + // Static builtin layer shared with the self-hosted compiler — single + // source of truth in runtime/ep_builtins.c (see runtime/ep_runtime.c note). + self.out.push_str(EP_BUILTINS_SRC); self.out.push_str("\n/* External Function Prototypes (FFI) */\n"); for ext in &program.externals { @@ -4095,6 +3933,7 @@ int main(int argc, char** argv) {{ } const RUNTIME_HEADER_AND_SRC: &str = include_str!("../runtime/ep_runtime.c"); +const EP_BUILTINS_SRC: &str = include_str!("../runtime/ep_builtins.c"); #[allow(dead_code)] const C_MAIN_BOOTSTRAPPER: &str = r#" From 8faaf8878a99d948f399f8563d8273a1f6d50940 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:10:02 +0100 Subject: [PATCH 04/51] GC: align conservative scan bases down to 8 bytes (UB on strict platforms) The scan bases come from char locals (&_marker / &_top_marker), so the void** walk could start misaligned -- undefined behaviour that skews the scan window on strict platforms (caught by valgrind on Linux; macOS/arm64 tolerated it silently). Mask the base DOWN: (uintptr_t)lo & ~(uintptr_t)7 in ep_gc_scan_own_stack_minor, and the same for *ep_thread_tops[t] in the major-path ep_gc_scan_thread_stacks. Aligning down only widens the conservative window by a few harmless bytes; aligning up could skip the slot holding a live root. Diagnosis trail (external session, credited): five falsified hypotheses -- WASM setjmp stub, register-only roots, unregistered main-thread bottom, init frame offset, hash- table dropout -- before valgrind + instrumentation exposed the odd scan window. Validated: 141-suite Smithian Fold corpus regenerated and green (826 checks), ex- crashers stressed 20x clean, heavy bisection proofs bounded. Co-Authored-By: Claude Opus 4.8 --- runtime/ep_runtime.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/runtime/ep_runtime.c b/runtime/ep_runtime.c index da41f56..6316c5e 100644 --- a/runtime/ep_runtime.c +++ b/runtime/ep_runtime.c @@ -1340,7 +1340,12 @@ static void ep_gc_scan_thread_stacks(void) { for (int t = 0; t < ep_num_threads; t++) { if (!ep_thread_active[t]) continue; if (!ep_thread_tops[t]) continue; - void** start = (void**)*ep_thread_tops[t]; + /* The published top comes from a char local, so it may not be pointer-aligned; + mask DOWN to 8 bytes. Aligning down only widens the conservative window by a + few harmless bytes — aligning up could skip the slot holding a live root. + Unaligned void** dereferences are UB and produce a skewed scan window on + strict platforms (caught by valgrind on Linux). */ + void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7); void** end = (void**)ep_thread_bottoms[t]; if (!start || !end) continue; if (start > end) { void** tmp = start; start = end; end = tmp; } @@ -1442,7 +1447,11 @@ static void ep_gc_scan_own_stack_minor(void) { char* a = (char*)(void*)&_marker; char* b = (char*)(void*)&_regs; char* lo = (a < b) ? a : b; - void** start = (void**)lo; + /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8 + bytes. Aligning down only widens the conservative window by a few harmless + bytes — aligning up could skip the slot holding a live root. Unaligned void** + dereferences are UB and skew the scan window on strict platforms (valgrind). */ + void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7); void** end = (void**)bottom; if (start > end) { void** tmp = start; start = end; end = tmp; } for (void** cur = start; cur < end; cur++) { From 79db0b2caa2b628f410ff22b31c72fce50709f54 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:18:49 +0100 Subject: [PATCH 05/51] feat(self-host): epc embeds the modern shared C runtime (generational GC) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-hosted compiler's emitted runtime was the OLD primitive collector (no-op push_root, single generation). It now embeds the same runtime text as the Rust compiler, via ep_runtime_gen.ep — generated from runtime/ep_runtime.c + runtime/ep_builtins.c by tools/gen_runtime_ep.ep (written in ErnosPlain). - get_c_runtime_source (1,462 lines of hand-maintained C-in-.ep) now delegates to get_shared_runtime_source(); round-trip verified byte-identical (169,133 chars) against the source files. - Renamed ep_codegen.ep's .ep-level helpers ep_int_to_str/string_contains to cg_* — they collided with the C builtins of the same names once the full runtime arrived. - This also delivers wholesale the builtins epc was missing (concat, string family, math/time, crypto, FFI dlopen/dlcall, fs, StringBuilder, float conversions…). Gates: 3-stage fixpoint byte-identical with the new runtime; parity 13→15/54; run_tests.sh 69/69 (Rust side untouched); tests/test_gc_remembered_uaf.ep (4-thread old→young churn) compiled BY EPC runs ASAN-clean, exit 0. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 1515 +----------- ep_runtime_gen.ep | 5055 +++++++++++++++++++++++++++++++++++++++ epc.ep | 1 + tools/gen_runtime_ep.ep | 128 + 4 files changed, 5213 insertions(+), 1486 deletions(-) create mode 100644 ep_runtime_gen.ep create mode 100644 tools/gen_runtime_ep.ep diff --git a/ep_codegen.ep b/ep_codegen.ep index edd5688..332f45e 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -68,7 +68,7 @@ define get_fn_c_name with func: return "_main" return name -define ep_int_to_str with n: +define cg_int_to_str with n: if n == 0: return "0" set lst to create_list() @@ -198,7 +198,7 @@ define get_new_label with state and prefix: set next_count to count + 1 set ok to set_list(state and 2 and next_count) - set num_str to ep_int_to_str(count) + set num_str to cg_int_to_str(count) set label_half to string_concat("L_" and prefix) set label to string_concat(label_half and "_") set final_label to string_concat(label and num_str) @@ -429,7 +429,7 @@ define is_global_var with name: set idx to idx + 1 return 1 -define string_contains with s and sub: +define cg_string_contains with s and sub: set len to string_length(s) set sub_len to string_length(sub) if sub_len > len: @@ -597,7 +597,7 @@ define gen_function with state and func: set j to 0 repeat while j < p_len: set struct_decl to string_concat(struct_decl and " long long arg") - set struct_decl to string_concat(struct_decl and ep_int_to_str(j)) + set struct_decl to string_concat(struct_decl and cg_int_to_str(j)) set struct_decl to string_concat(struct_decl and ";\n") set j to j + 1 if p_len == 0: @@ -624,7 +624,7 @@ define gen_function with state and func: set j to 0 repeat while j < p_len: set wrap_fn to string_concat(wrap_fn and "args->arg") - set wrap_fn to string_concat(wrap_fn and ep_int_to_str(j)) + set wrap_fn to string_concat(wrap_fn and cg_int_to_str(j)) if j < p_len - 1: set wrap_fn to string_concat(wrap_fn and ", ") set j to j + 1 @@ -670,7 +670,7 @@ define gen_function with state and func: set p_node to get_list(params and j) set p_name to get_list(p_node and 0) set pub_fn to string_concat(pub_fn and " args->arg") - set pub_fn to string_concat(pub_fn and ep_int_to_str(j)) + set pub_fn to string_concat(pub_fn and cg_int_to_str(j)) set pub_fn to string_concat(pub_fn and " = ") set pub_fn to string_concat(pub_fn and p_name) set pub_fn to string_concat(pub_fn and ";\n") @@ -775,7 +775,7 @@ define gen_function with state and func: # Emit cleanup label and deallocations set ok to emit(state and "L_cleanup:\n") if gc_root_count > 0: - set gc_count_str to ep_int_to_str(gc_root_count) + set gc_count_str to cg_int_to_str(gc_root_count) set root_pop to " ep_gc_pop_roots(" set root_pop to string_concat(root_pop and gc_count_str) set root_pop to string_concat(root_pop and ");\n") @@ -954,9 +954,9 @@ define gen_statement with state and stmt and var_keys and var_values: set ok to emit(state and " {\n") set line to " spawn_args_" - set line to string_concat(line and ep_int_to_str(idx_val)) + set line to string_concat(line and cg_int_to_str(idx_val)) set line to string_concat(line and "* s_args = malloc(sizeof(spawn_args_") - set line to string_concat(line and ep_int_to_str(idx_val)) + set line to string_concat(line and cg_int_to_str(idx_val)) set line to string_concat(line and "));\n") set ok to emit(state and line) @@ -965,7 +965,7 @@ define gen_statement with state and stmt and var_keys and var_values: set arg to get_list(args and j) set arg_str to gen_expr(state and arg and var_keys and var_values) set line to " s_args->arg" - set line to string_concat(line and ep_int_to_str(j)) + set line to string_concat(line and cg_int_to_str(j)) set line to string_concat(line and " = ") set line to string_concat(line and arg_str) set line to string_concat(line and ";\n") @@ -974,7 +974,7 @@ define gen_statement with state and stmt and var_keys and var_values: set ok to emit(state and " pthread_t t;\n") set line to " pthread_create(&t, NULL, spawn_wrapper_" - set line to string_concat(line and ep_int_to_str(idx_val)) + set line to string_concat(line and cg_int_to_str(idx_val)) set line to string_concat(line and ", s_args);\n") set ok to emit(state and line) set ok to emit(state and " pthread_detach(t);\n") @@ -1054,7 +1054,7 @@ define gen_statement with state and stmt and var_keys and var_values: set line to " long long " set line to string_concat(line and bname) set line to string_concat(line and " = ((long long*)_match_val)[") - set line to string_concat(line and ep_int_to_str(b_idx + 1)) + set line to string_concat(line and cg_int_to_str(b_idx + 1)) set line to string_concat(line and "];\n") set ok to emit(state and line) set b_idx to b_idx + 1 @@ -1123,7 +1123,7 @@ define gen_expr with state and expr and var_keys and var_values: if type == 1: # NODE_INT set val to get_list(expr and 1) - set num_str to ep_int_to_str(val) + set num_str to cg_int_to_str(val) return string_concat(num_str and "LL") if type == 2: # NODE_STR @@ -1389,7 +1389,7 @@ define gen_expr with state and expr and var_keys and var_values: set args_len to length_list(args) set alloc_size to args_len + 1 set res to "({ long long* _v = (long long*)malloc(sizeof(long long) * " - set res to string_concat(res and ep_int_to_str(alloc_size)) + set res to string_concat(res and cg_int_to_str(alloc_size)) set res to string_concat(res and "); _v[0] = EP_TAG_") set res to string_concat(res and variant_name) set res to string_concat(res and "; ") @@ -1398,7 +1398,7 @@ define gen_expr with state and expr and var_keys and var_values: set arg to get_list(args and a_idx) set arg_str to gen_expr(state and arg and var_keys and var_values) set res to string_concat(res and "_v[") - set res to string_concat(res and ep_int_to_str(a_idx + 1)) + set res to string_concat(res and cg_int_to_str(a_idx + 1)) set res to string_concat(res and "] = ") set res to string_concat(res and arg_str) set res to string_concat(res and "; ") @@ -1451,1467 +1451,10 @@ define gen_expr with state and expr and var_keys and var_values: return "" define get_c_runtime_source: - set lines to create_list() - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "#ifdef _WIN32\n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #pragma comment(lib, \"ws2_32.lib\")\n") - set ok to append_list(lines and " typedef HANDLE ep_thread_t;\n") - set ok to append_list(lines and " typedef CRITICAL_SECTION ep_mutex_t;\n") - set ok to append_list(lines and " typedef CONDITION_VARIABLE ep_cond_t;\n") - set ok to append_list(lines and " #define ep_mutex_init(m) InitializeCriticalSection(m)\n") - set ok to append_list(lines and " #define ep_mutex_lock(m) EnterCriticalSection(m)\n") - set ok to append_list(lines and " #define ep_mutex_unlock(m) LeaveCriticalSection(m)\n") - set ok to append_list(lines and " #define ep_cond_init(c) InitializeConditionVariable(c)\n") - set ok to append_list(lines and " #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)\n") - set ok to append_list(lines and " #define ep_cond_signal(c) WakeConditionVariable(c)\n") - set ok to append_list(lines and "#else\n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " #include \n") - set ok to append_list(lines and " typedef pthread_t ep_thread_t;\n") - set ok to append_list(lines and " typedef pthread_mutex_t ep_mutex_t;\n") - set ok to append_list(lines and " typedef pthread_cond_t ep_cond_t;\n") - set ok to append_list(lines and " #define ep_mutex_init(m) pthread_mutex_init(m, NULL)\n") - set ok to append_list(lines and " #define ep_mutex_lock(m) pthread_mutex_lock(m)\n") - set ok to append_list(lines and " #define ep_mutex_unlock(m) pthread_mutex_unlock(m)\n") - set ok to append_list(lines and " #define ep_cond_init(c) pthread_cond_init(c, NULL)\n") - set ok to append_list(lines and " #define ep_cond_wait(c, m) pthread_cond_wait(c, m)\n") - set ok to append_list(lines and " #define ep_cond_signal(c) pthread_cond_signal(c)\n") - set ok to append_list(lines and "#endif\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef enum {\n") - set ok to append_list(lines and " EP_OBJ_LIST,\n") - set ok to append_list(lines and " EP_OBJ_STRING,\n") - set ok to append_list(lines and " EP_OBJ_STRUCT,\n") - set ok to append_list(lines and " EP_OBJ_CLOSURE\n") - set ok to append_list(lines and "} EpObjKind;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct EpGCObject {\n") - set ok to append_list(lines and " EpObjKind kind;\n") - set ok to append_list(lines and " int marked;\n") - set ok to append_list(lines and " void* ptr; /* actual allocation pointer */\n") - set ok to append_list(lines and " long long size; /* payload size for structs */\n") - set ok to append_list(lines and " long long num_fields; /* number of fields for structs (each is long long) */\n") - set ok to append_list(lines and " struct EpGCObject* next; /* intrusive linked list */\n") - set ok to append_list(lines and "} EpGCObject;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " long long chan;\n") - set ok to append_list(lines and " int completed;\n") - set ok to append_list(lines and " long long value;\n") - set ok to append_list(lines and "} EpFuture;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* GC globals */\n") - set ok to append_list(lines and "static EpGCObject* ep_gc_head = NULL;\n") - set ok to append_list(lines and "static long long ep_gc_count = 0;\n") - set ok to append_list(lines and "static long long ep_gc_threshold = 8192;\n") - set ok to append_list(lines and "static int ep_gc_enabled = 1;\n") - set ok to append_list(lines and "static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;\n") - set ok to append_list(lines and "/* Stop-the-world coordination: the collector sets ep_gc_stop_requested and\n") - set ok to append_list(lines and " waits (ep_gc_stop_the_world) for every other thread to park at a safepoint\n") - set ok to append_list(lines and " before the conservative stack scan, so it never scans a running thread's\n") - set ok to append_list(lines and " stack. Touched only under ep_gc_mutex (the lock-free reads at safepoints are\n") - set ok to append_list(lines and " a benign optimization, covered by the collector's bounded wait). */\n") - set ok to append_list(lines and "static volatile int ep_gc_stop_requested = 0;\n") - set ok to append_list(lines and "static int ep_gc_parked_count = 0;\n") - set ok to append_list(lines and "static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Thread registry for conservative stack scanning in multi-threaded environment */\n") - set ok to append_list(lines and "#define EP_MAX_THREADS 256\n") - set ok to append_list(lines and "static __thread void* volatile ep_thread_local_top = NULL;\n") - set ok to append_list(lines and "static __thread void* ep_thread_local_bottom = NULL;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n") - set ok to append_list(lines and "static void* ep_thread_bottoms[EP_MAX_THREADS];\n") - set ok to append_list(lines and "static int ep_thread_active[EP_MAX_THREADS];\n") - set ok to append_list(lines and "static int ep_num_threads = 0;\n") - set ok to append_list(lines and "static pthread_mutex_t ep_thread_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void ep_gc_register_thread(void* stack_bottom) {\n") - set ok to append_list(lines and " ep_thread_local_bottom = stack_bottom;\n") - set ok to append_list(lines and " ep_thread_local_top = stack_bottom;\n") - set ok to append_list(lines and " \n") - set ok to append_list(lines and " pthread_mutex_lock(&ep_thread_registry_mutex);\n") - set ok to append_list(lines and " int slot = -1;\n") - set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n") - set ok to append_list(lines and " if (!ep_thread_active[i]) {\n") - set ok to append_list(lines and " slot = i;\n") - set ok to append_list(lines and " break;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {\n") - set ok to append_list(lines and " slot = ep_num_threads++;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " if (slot != -1) {\n") - set ok to append_list(lines and " ep_thread_tops[slot] = &ep_thread_local_top;\n") - set ok to append_list(lines and " ep_thread_bottoms[slot] = stack_bottom;\n") - set ok to append_list(lines and " ep_thread_active[slot] = 1;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_thread_registry_mutex);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void ep_gc_unregister_thread(void) {\n") - set ok to append_list(lines and " pthread_mutex_lock(&ep_thread_registry_mutex);\n") - set ok to append_list(lines and " pthread_t self = pthread_self();\n") - set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n") - set ok to append_list(lines and " if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n") - set ok to append_list(lines and " ep_thread_active[i] = 0;\n") - set ok to append_list(lines and " break;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_thread_registry_mutex);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Park the calling thread if the collector has stopped the world. MUST hold\n") - set ok to append_list(lines and " ep_gc_mutex. Spills registers and publishes the stack top first so the\n") - set ok to append_list(lines and " conservative scan covers this thread's live frame while it is parked. */\n") - set ok to append_list(lines and "static void ep_gc_park_if_stopped(void) {\n") - set ok to append_list(lines and " if (!ep_gc_stop_requested) return;\n") - set ok to append_list(lines and " jmp_buf _pregs;\n") - set ok to append_list(lines and " memset(&_pregs, 0, sizeof(_pregs));\n") - set ok to append_list(lines and " setjmp(_pregs); /* spill registers onto this thread's stack */\n") - set ok to append_list(lines and " EP_GC_UPDATE_TOP(); /* publish stack top (below _pregs) for the scan */\n") - set ok to append_list(lines and " __sync_synchronize();\n") - set ok to append_list(lines and " ep_gc_parked_count++;\n") - set ok to append_list(lines and " while (ep_gc_stop_requested) {\n") - set ok to append_list(lines and " pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " ep_gc_parked_count--;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Begin a stop-the-world pause. MUST hold ep_gc_mutex. Briefly releases the\n") - set ok to append_list(lines and " lock so blocked mutators can reach a safepoint and park. After ~10ms it\n") - set ok to append_list(lines and " proceeds anyway: a thread that has not parked is blocked/idle with a stable\n") - set ok to append_list(lines and " stack, so scanning it is still safe in practice. */\n") - set ok to append_list(lines and "static void ep_gc_stop_the_world(void) {\n") - set ok to append_list(lines and " ep_gc_stop_requested = 1;\n") - set ok to append_list(lines and " for (int spins = 0; spins < 40; spins++) {\n") - set ok to append_list(lines and " int others = 0;\n") - set ok to append_list(lines and " for (int t = 0; t < ep_num_threads; t++) {\n") - set ok to append_list(lines and " if (ep_thread_active[t] && ep_thread_tops[t] != &ep_thread_local_top) others++;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " if (others <= 0 || ep_gc_parked_count >= others) return;\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") - set ok to append_list(lines and " usleep(250);\n") - set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */\n") - set ok to append_list(lines and "static void ep_gc_start_the_world(void) {\n") - set ok to append_list(lines and " ep_gc_stop_requested = 0;\n") - set ok to append_list(lines and " pthread_cond_broadcast(&ep_gc_resume_cond);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " void* key;\n") - set ok to append_list(lines and " EpGCObject* value;\n") - set ok to append_list(lines and "} EpGCEntry;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static EpGCEntry* ep_gc_table = NULL;\n") - set ok to append_list(lines and "static long long ep_gc_table_cap = 0;\n") - set ok to append_list(lines and "static long long ep_gc_table_size = 0;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void ep_gc_table_insert(void* key, EpGCObject* value) {\n") - set ok to append_list(lines and " if (ep_gc_table_size * 2 >= ep_gc_table_cap) {\n") - set ok to append_list(lines and " long long old_cap = ep_gc_table_cap;\n") - set ok to append_list(lines and " long long new_cap = old_cap == 0 ? 512 : old_cap * 2;\n") - set ok to append_list(lines and " EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));\n") - set ok to append_list(lines and " for (long long i = 0; i < old_cap; i++) {\n") - set ok to append_list(lines and " if (ep_gc_table[i].key != NULL) {\n") - set ok to append_list(lines and " long long idx = ((uintptr_t)ep_gc_table[i].key) % new_cap;\n") - set ok to append_list(lines and " while (new_table[idx].key != NULL) {\n") - set ok to append_list(lines and " idx = (idx + 1) % new_cap;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " new_table[idx] = ep_gc_table[i];\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " free(ep_gc_table);\n") - set ok to append_list(lines and " ep_gc_table = new_table;\n") - set ok to append_list(lines and " ep_gc_table_cap = new_cap;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " \n") - set ok to append_list(lines and " long long idx = ((uintptr_t)key) % ep_gc_table_cap;\n") - set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n") - set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n") - set ok to append_list(lines and " ep_gc_table[idx].value = value;\n") - set ok to append_list(lines and " return;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " idx = (idx + 1) % ep_gc_table_cap;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " ep_gc_table[idx].key = key;\n") - set ok to append_list(lines and " ep_gc_table[idx].value = value;\n") - set ok to append_list(lines and " ep_gc_table_size++;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static EpGCObject* ep_gc_table_get(void* key) {\n") - set ok to append_list(lines and " if (ep_gc_table_cap == 0) return NULL;\n") - set ok to append_list(lines and " long long idx = ((uintptr_t)key) % ep_gc_table_cap;\n") - set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n") - set ok to append_list(lines and " if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;\n") - set ok to append_list(lines and " idx = (idx + 1) % ep_gc_table_cap;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return NULL;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void ep_gc_table_remove(void* key) {\n") - set ok to append_list(lines and " if (ep_gc_table_cap == 0) return;\n") - set ok to append_list(lines and " long long idx = ((uintptr_t)key) % ep_gc_table_cap;\n") - set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n") - set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n") - set ok to append_list(lines and " ep_gc_table[idx].key = NULL;\n") - set ok to append_list(lines and " ep_gc_table[idx].value = NULL;\n") - set ok to append_list(lines and " ep_gc_table_size--;\n") - set ok to append_list(lines and " long long next_idx = (idx + 1) % ep_gc_table_cap;\n") - set ok to append_list(lines and " while (ep_gc_table[next_idx].key != NULL) {\n") - set ok to append_list(lines and " void* rehash_key = ep_gc_table[next_idx].key;\n") - set ok to append_list(lines and " EpGCObject* rehash_val = ep_gc_table[next_idx].value;\n") - set ok to append_list(lines and " ep_gc_table[next_idx].key = NULL;\n") - set ok to append_list(lines and " ep_gc_table[next_idx].value = NULL;\n") - set ok to append_list(lines and " ep_gc_table_size--;\n") - set ok to append_list(lines and " ep_gc_table_insert(rehash_key, rehash_val);\n") - set ok to append_list(lines and " next_idx = (next_idx + 1) % ep_gc_table_cap;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " idx = (idx + 1) % ep_gc_table_cap;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Dummy shadow stack API for compatibility with compiler-generated code */\n") - set ok to append_list(lines and "static void ep_gc_push_root(long long* root) { (void)root; }\n") - set ok to append_list(lines and "static void ep_gc_pop_roots(long long count) { (void)count; }\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Register a new GC object */\n") - set ok to append_list(lines and "static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {\n") - set ok to append_list(lines and " if (!ptr) return NULL;\n") - set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") - set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */\n") - set ok to append_list(lines and " EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));\n") - set ok to append_list(lines and " if (!obj) {\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") - set ok to append_list(lines and " return NULL;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " obj->kind = kind;\n") - set ok to append_list(lines and " obj->marked = 0;\n") - set ok to append_list(lines and " obj->ptr = ptr;\n") - set ok to append_list(lines and " obj->size = 0;\n") - set ok to append_list(lines and " obj->num_fields = 0;\n") - set ok to append_list(lines and " obj->next = ep_gc_head;\n") - set ok to append_list(lines and " ep_gc_head = obj;\n") - set ok to append_list(lines and " ep_gc_count++;\n") - set ok to append_list(lines and " ep_gc_table_insert(ptr, obj);\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") - set ok to append_list(lines and " return obj;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Find GC object by pointer */\n") - set ok to append_list(lines and "static EpGCObject* ep_gc_find(void* ptr) {\n") - set ok to append_list(lines and " return ep_gc_table_get(ptr);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Forward declarations for list type (needed by GC mark) */\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " long long* data;\n") - set ok to append_list(lines and " long long length;\n") - set ok to append_list(lines and " long long capacity;\n") - set ok to append_list(lines and "} EpList;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Mark a single object and recursively mark its children */\n") - set ok to append_list(lines and "static void ep_gc_mark_object(void* ptr) {\n") - set ok to append_list(lines and " if (!ptr) return;\n") - set ok to append_list(lines and " EpGCObject* obj = ep_gc_find(ptr);\n") - set ok to append_list(lines and " if (!obj || obj->marked) return;\n") - set ok to append_list(lines and " obj->marked = 1;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " if (obj->kind == EP_OBJ_LIST) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)ptr;\n") - set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") - set ok to append_list(lines and " long long val = list->data[i];\n") - set ok to append_list(lines and " if (val != 0) {\n") - set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_STRUCT) {\n") - set ok to append_list(lines and " long long* fields = (long long*)ptr;\n") - set ok to append_list(lines and " for (long long i = 0; i < obj->num_fields; i++) {\n") - set ok to append_list(lines and " if (fields[i] != 0) {\n") - set ok to append_list(lines and " ep_gc_mark_object((void*)fields[i]);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Mark phase: traverse from stack roots of all registered threads */\n") - set ok to append_list(lines and "static void ep_gc_mark(void) {\n") - set ok to append_list(lines and " jmp_buf regs;\n") - set ok to append_list(lines and " memset(®s, 0, sizeof(regs));\n") - set ok to append_list(lines and " setjmp(regs); /* Spill registers of the current thread */\n") - set ok to append_list(lines and " \n") - set ok to append_list(lines and " // Update stack top of current thread\n") - set ok to append_list(lines and " volatile void* stack_top;\n") - set ok to append_list(lines and " stack_top = (void*)&stack_top;\n") - set ok to append_list(lines and " ep_thread_local_top = (void*)stack_top;\n") - set ok to append_list(lines and " \n") - set ok to append_list(lines and " pthread_mutex_lock(&ep_thread_registry_mutex);\n") - set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n") - set ok to append_list(lines and " if (ep_thread_active[i]) {\n") - set ok to append_list(lines and " void** start = (void**)*ep_thread_tops[i];\n") - set ok to append_list(lines and " void** end = (void**)ep_thread_bottoms[i];\n") - set ok to append_list(lines and " if (start && end) {\n") - set ok to append_list(lines and " if (start > end) {\n") - set ok to append_list(lines and " void** tmp = start;\n") - set ok to append_list(lines and " start = end;\n") - set ok to append_list(lines and " end = tmp;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " for (void** cur = start; cur < end; cur++) {\n") - set ok to append_list(lines and " void* ptr = *cur;\n") - set ok to append_list(lines and " if (ptr) {\n") - set ok to append_list(lines and " ep_gc_mark_object(ptr);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_thread_registry_mutex);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Sweep phase: free unmarked objects */\n") - set ok to append_list(lines and "static void ep_gc_sweep(void) {\n") - set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n") - set ok to append_list(lines and " while (*cur) {\n") - set ok to append_list(lines and " if (!(*cur)->marked) {\n") - set ok to append_list(lines and " EpGCObject* garbage = *cur;\n") - set ok to append_list(lines and " *cur = garbage->next;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " ep_gc_table_remove(garbage->ptr);\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " if (garbage->kind == EP_OBJ_LIST) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)garbage->ptr;\n") - set ok to append_list(lines and " if (list) {\n") - set ok to append_list(lines and " free(list->data);\n") - set ok to append_list(lines and " free(list);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRING) {\n") - set ok to append_list(lines and " free(garbage->ptr);\n") - set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRUCT) {\n") - set ok to append_list(lines and " free(garbage->ptr);\n") - set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_CLOSURE) {\n") - set ok to append_list(lines and " free(garbage->ptr);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " free(garbage);\n") - set ok to append_list(lines and " ep_gc_count--;\n") - set ok to append_list(lines and " } else {\n") - set ok to append_list(lines and " (*cur)->marked = 0; /* reset for next cycle */\n") - set ok to append_list(lines and " cur = &(*cur)->next;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Run a full GC collection */\n") - set ok to append_list(lines and "static void ep_gc_collect(void) {\n") - set ok to append_list(lines and " if (!ep_gc_enabled) return;\n") - set ok to append_list(lines and " ep_gc_mark();\n") - set ok to append_list(lines and " ep_gc_sweep();\n") - set ok to append_list(lines and " ep_gc_threshold = ep_gc_count * 2;\n") - set ok to append_list(lines and " if (ep_gc_threshold < 8192) ep_gc_threshold = 8192;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Maybe trigger GC if we've exceeded threshold */\n") - set ok to append_list(lines and "static void ep_gc_maybe_collect(void) {\n") - set ok to append_list(lines and " EP_GC_UPDATE_TOP();\n") - set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") - set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: park if another thread is collecting */\n") - set ok to append_list(lines and " if (ep_gc_count >= ep_gc_threshold) {\n") - set ok to append_list(lines and " ep_gc_stop_the_world();\n") - set ok to append_list(lines and " ep_gc_collect();\n") - set ok to append_list(lines and " ep_gc_start_the_world();\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Unregister an object (for explicit free — removes from GC tracking) */\n") - set ok to append_list(lines and "static void ep_gc_unregister(void* ptr) {\n") - set ok to append_list(lines and " if (!ptr) return;\n") - set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") - set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */\n") - set ok to append_list(lines and " ep_gc_table_remove(ptr);\n") - set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n") - set ok to append_list(lines and " while (*cur) {\n") - set ok to append_list(lines and " if ((*cur)->ptr == ptr) {\n") - set ok to append_list(lines and " EpGCObject* found = *cur;\n") - set ok to append_list(lines and " *cur = found->next;\n") - set ok to append_list(lines and " free(found);\n") - set ok to append_list(lines and " ep_gc_count--;\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") - set ok to append_list(lines and " return;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " cur = &(*cur)->next;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Cleanup all remaining GC objects (called at program exit) */\n") - set ok to append_list(lines and "static void ep_gc_shutdown(void) {\n") - set ok to append_list(lines and " ep_gc_enabled = 0;\n") - set ok to append_list(lines and " EpGCObject* cur = ep_gc_head;\n") - set ok to append_list(lines and " while (cur) {\n") - set ok to append_list(lines and " EpGCObject* next = cur->next;\n") - set ok to append_list(lines and " if (cur->kind == EP_OBJ_LIST) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)cur->ptr;\n") - set ok to append_list(lines and " if (list) { free(list->data); free(list); }\n") - set ok to append_list(lines and " } else {\n") - set ok to append_list(lines and " free(cur->ptr);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " free(cur);\n") - set ok to append_list(lines and " cur = next;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " ep_gc_head = NULL;\n") - set ok to append_list(lines and " ep_gc_count = 0;\n") - set ok to append_list(lines and " if (ep_gc_table) {\n") - set ok to append_list(lines and " free(ep_gc_table);\n") - set ok to append_list(lines and " ep_gc_table = NULL;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " ep_gc_table_cap = 0;\n") - set ok to append_list(lines and " ep_gc_table_size = 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* ========== End Garbage Collector ========== */\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long create_list(void);\n") - set ok to append_list(lines and "long long append_list(long long list_ptr, long long value);\n") - set ok to append_list(lines and "long long get_list(long long list_ptr, long long index);\n") - set ok to append_list(lines and "long long set_list(long long list_ptr, long long index, long long value);\n") - set ok to append_list(lines and "long long length_list(long long list_ptr);\n") - set ok to append_list(lines and "long long free_list(long long list_ptr);\n") - set ok to append_list(lines and "long long pop_list(long long list_ptr);\n") - set ok to append_list(lines and "char* string_from_list(long long list_ptr);\n") - set ok to append_list(lines and "long long string_length(const char* s);\n") - set ok to append_list(lines and "long long display_string(const char* s);\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " long long* data;\n") - set ok to append_list(lines and " long long capacity;\n") - set ok to append_list(lines and " long long head;\n") - set ok to append_list(lines and " long long tail;\n") - set ok to append_list(lines and " long long size;\n") - set ok to append_list(lines and " ep_mutex_t mutex;\n") - set ok to append_list(lines and " ep_cond_t cond_recv;\n") - set ok to append_list(lines and " ep_cond_t cond_send;\n") - set ok to append_list(lines and "} EpChannel;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long create_channel(void) {\n") - set ok to append_list(lines and " EpChannel* chan = malloc(sizeof(EpChannel));\n") - set ok to append_list(lines and " if (!chan) return 0;\n") - set ok to append_list(lines and " chan->capacity = 1024;\n") - set ok to append_list(lines and " chan->data = malloc(chan->capacity * sizeof(long long));\n") - set ok to append_list(lines and " chan->head = 0;\n") - set ok to append_list(lines and " chan->tail = 0;\n") - set ok to append_list(lines and " chan->size = 0;\n") - set ok to append_list(lines and " ep_mutex_init(&chan->mutex);\n") - set ok to append_list(lines and " ep_cond_init(&chan->cond_recv);\n") - set ok to append_list(lines and " ep_cond_init(&chan->cond_send);\n") - set ok to append_list(lines and " return (long long)chan;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long send_channel(long long chan_ptr, long long value) {\n") - set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n") - set ok to append_list(lines and " if (!chan) return 0;\n") - set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") - set ok to append_list(lines and " while (chan->size >= chan->capacity) {\n") - set ok to append_list(lines and " ep_cond_wait(&chan->cond_send, &chan->mutex);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " chan->data[chan->tail] = value;\n") - set ok to append_list(lines and " chan->tail = (chan->tail + 1) % chan->capacity;\n") - set ok to append_list(lines and " chan->size += 1;\n") - set ok to append_list(lines and " ep_cond_signal(&chan->cond_recv);\n") - set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long receive_channel(long long chan_ptr) {\n") - set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n") - set ok to append_list(lines and " if (!chan) return 0;\n") - set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") - set ok to append_list(lines and " while (chan->size <= 0) {\n") - set ok to append_list(lines and " ep_cond_wait(&chan->cond_recv, &chan->mutex);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " long long value = chan->data[chan->head];\n") - set ok to append_list(lines and " chan->head = (chan->head + 1) % chan->capacity;\n") - set ok to append_list(lines and " chan->size -= 1;\n") - set ok to append_list(lines and " ep_cond_signal(&chan->cond_send);\n") - set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_net_connect(const char* host, long long port) {\n") - set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n") - set ok to append_list(lines and " if (sockfd < 0) return -1;\n") - set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n") - set ok to append_list(lines and " if (!server) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return -1;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") - set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") - set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") - set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n") - set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") - set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return -1;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return sockfd;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_net_listen(long long port) {\n") - set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n") - set ok to append_list(lines and " if (sockfd < 0) return -1;\n") - set ok to append_list(lines and " int opt = 1;\n") - set ok to append_list(lines and " setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));\n") - set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") - set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") - set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") - set ok to append_list(lines and " serv_addr.sin_addr.s_addr = INADDR_ANY;\n") - set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") - set ok to append_list(lines and " if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return -1;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " if (listen(sockfd, 10) < 0) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return -1;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return sockfd;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_net_accept(long long server_fd) {\n") - set ok to append_list(lines and " struct sockaddr_in cli_addr;\n") - set ok to append_list(lines and " socklen_t clilen = sizeof(cli_addr);\n") - set ok to append_list(lines and " int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);\n") - set ok to append_list(lines and " return newsockfd;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_net_send(long long fd, const char* data) {\n") - set ok to append_list(lines and " if (!data) return 0;\n") - set ok to append_list(lines and " return send((int)fd, data, strlen(data), 0);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "char* ep_net_recv(long long fd, long long max_len) {\n") - set ok to append_list(lines and " char* buf = malloc(max_len + 1);\n") - set ok to append_list(lines and " if (!buf) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " ssize_t n = recv((int)fd, buf, max_len, 0);\n") - set ok to append_list(lines and " if (n < 0) n = 0;\n") - set ok to append_list(lines and " buf[n] = '\\0';\n") - set ok to append_list(lines and " return buf;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_net_close(long long fd) {\n") - set ok to append_list(lines and " return close((int)fd);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_sleep_ms(long long ms) {\n") - set ok to append_list(lines and " usleep((useconds_t)(ms * 1000));\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "unsigned long hash_string(const char* str) {\n") - set ok to append_list(lines and " unsigned long hash = 5381;\n") - set ok to append_list(lines and " int c;\n") - set ok to append_list(lines and " while ((c = *str++)) {\n") - set ok to append_list(lines and " hash = ((hash << 5) + hash) + c;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return hash;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " char* key;\n") - set ok to append_list(lines and " long long value;\n") - set ok to append_list(lines and " int used;\n") - set ok to append_list(lines and "} EpMapEntry;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " EpMapEntry* entries;\n") - set ok to append_list(lines and " long long capacity;\n") - set ok to append_list(lines and " long long size;\n") - set ok to append_list(lines and "} EpMap;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long create_map(void) {\n") - set ok to append_list(lines and " EpMap* map = malloc(sizeof(EpMap));\n") - set ok to append_list(lines and " if (!map) return 0;\n") - set ok to append_list(lines and " map->capacity = 16;\n") - set ok to append_list(lines and " map->size = 0;\n") - set ok to append_list(lines and " map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n") - set ok to append_list(lines and " if (!map->entries) {\n") - set ok to append_list(lines and " free(map);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return (long long)map;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void map_resize(EpMap* map, long long new_capacity) {\n") - set ok to append_list(lines and " EpMapEntry* old_entries = map->entries;\n") - set ok to append_list(lines and " long long old_capacity = map->capacity;\n") - set ok to append_list(lines and " map->capacity = new_capacity;\n") - set ok to append_list(lines and " map->entries = calloc(new_capacity, sizeof(EpMapEntry));\n") - set ok to append_list(lines and " map->size = 0;\n") - set ok to append_list(lines and " for (long long i = 0; i < old_capacity; i++) {\n") - set ok to append_list(lines and " if (old_entries[i].used && old_entries[i].key != NULL) {\n") - set ok to append_list(lines and " char* key = old_entries[i].key;\n") - set ok to append_list(lines and " long long value = old_entries[i].value;\n") - set ok to append_list(lines and " unsigned long h = hash_string(key) % new_capacity;\n") - set ok to append_list(lines and " while (map->entries[h].used) {\n") - set ok to append_list(lines and " h = (h + 1) % new_capacity;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " map->entries[h].key = key;\n") - set ok to append_list(lines and " map->entries[h].value = value;\n") - set ok to append_list(lines and " map->entries[h].used = 1;\n") - set ok to append_list(lines and " map->size++;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " free(old_entries);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long map_insert(long long map_ptr, long long key_val, long long value) {\n") - set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") - set ok to append_list(lines and " const char* key = (const char*)key_val;\n") - set ok to append_list(lines and " if (!map || !key) return 0;\n") - set ok to append_list(lines and " if (map->size * 2 >= map->capacity) {\n") - set ok to append_list(lines and " map_resize(map, map->capacity * 2);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") - set ok to append_list(lines and " while (map->entries[h].used) {\n") - set ok to append_list(lines and " if (strcmp(map->entries[h].key, key) == 0) {\n") - set ok to append_list(lines and " map->entries[h].value = value;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " map->entries[h].key = strdup(key);\n") - set ok to append_list(lines and " map->entries[h].value = value;\n") - set ok to append_list(lines and " map->entries[h].used = 1;\n") - set ok to append_list(lines and " map->size++;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long map_get_val(long long map_ptr, long long key_val) {\n") - set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") - set ok to append_list(lines and " const char* key = (const char*)key_val;\n") - set ok to append_list(lines and " if (!map || !key) return 0;\n") - set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") - set ok to append_list(lines and " long long start_h = h;\n") - set ok to append_list(lines and " while (map->entries[h].used) {\n") - set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n") - set ok to append_list(lines and " return map->entries[h].value;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") - set ok to append_list(lines and " if (h == start_h) break;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long map_contains(long long map_ptr, long long key_val) {\n") - set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") - set ok to append_list(lines and " const char* key = (const char*)key_val;\n") - set ok to append_list(lines and " if (!map || !key) return 0;\n") - set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") - set ok to append_list(lines and " long long start_h = h;\n") - set ok to append_list(lines and " while (map->entries[h].used) {\n") - set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n") - set ok to append_list(lines and " return 1;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") - set ok to append_list(lines and " if (h == start_h) break;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long map_delete(long long map_ptr, long long key_val) {\n") - set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") - set ok to append_list(lines and " const char* key = (const char*)key_val;\n") - set ok to append_list(lines and " if (!map || !key) return 0;\n") - set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") - set ok to append_list(lines and " long long start_h = h;\n") - set ok to append_list(lines and " while (map->entries[h].used) {\n") - set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n") - set ok to append_list(lines and " free(map->entries[h].key);\n") - set ok to append_list(lines and " map->entries[h].key = NULL;\n") - set ok to append_list(lines and " map->entries[h].value = 0;\n") - set ok to append_list(lines and " map->entries[h].used = 0;\n") - set ok to append_list(lines and " map->size--;\n") - set ok to append_list(lines and " long long next_h = (h + 1) % map->capacity;\n") - set ok to append_list(lines and " while (map->entries[next_h].used) {\n") - set ok to append_list(lines and " char* k = map->entries[next_h].key;\n") - set ok to append_list(lines and " long long v = map->entries[next_h].value;\n") - set ok to append_list(lines and " map->entries[next_h].key = NULL;\n") - set ok to append_list(lines and " map->entries[next_h].value = 0;\n") - set ok to append_list(lines and " map->entries[next_h].used = 0;\n") - set ok to append_list(lines and " map->size--;\n") - set ok to append_list(lines and " map_insert(map_ptr, (long long)k, v);\n") - set ok to append_list(lines and " free(k);\n") - set ok to append_list(lines and " next_h = (next_h + 1) % map->capacity;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return 1;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") - set ok to append_list(lines and " if (h == start_h) break;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long free_map(long long map_ptr) {\n") - set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") - set ok to append_list(lines and " if (!map) return 0;\n") - set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n") - set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n") - set ok to append_list(lines and " free(map->entries[i].key);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " free(map->entries);\n") - set ok to append_list(lines and " free(map);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " long long* data;\n") - set ok to append_list(lines and " long long capacity;\n") - set ok to append_list(lines and " long long head;\n") - set ok to append_list(lines and " long long tail;\n") - set ok to append_list(lines and " long long size;\n") - set ok to append_list(lines and "} EpDeque;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long create_deque(void) {\n") - set ok to append_list(lines and " EpDeque* dq = malloc(sizeof(EpDeque));\n") - set ok to append_list(lines and " if (!dq) return 0;\n") - set ok to append_list(lines and " dq->capacity = 16;\n") - set ok to append_list(lines and " dq->size = 0;\n") - set ok to append_list(lines and " dq->head = 0;\n") - set ok to append_list(lines and " dq->tail = 0;\n") - set ok to append_list(lines and " dq->data = malloc(dq->capacity * sizeof(long long));\n") - set ok to append_list(lines and " if (!dq->data) {\n") - set ok to append_list(lines and " free(dq);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return (long long)dq;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void deque_resize(EpDeque* dq, long long new_capacity) {\n") - set ok to append_list(lines and " long long* new_data = malloc(new_capacity * sizeof(long long));\n") - set ok to append_list(lines and " for (long long i = 0; i < dq->size; i++) {\n") - set ok to append_list(lines and " new_data[i] = dq->data[(dq->head + i) % dq->capacity];\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " free(dq->data);\n") - set ok to append_list(lines and " dq->data = new_data;\n") - set ok to append_list(lines and " dq->capacity = new_capacity;\n") - set ok to append_list(lines and " dq->head = 0;\n") - set ok to append_list(lines and " dq->tail = dq->size;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long deque_push_back(long long dq_ptr, long long value) {\n") - set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") - set ok to append_list(lines and " if (!dq) return 0;\n") - set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n") - set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " dq->data[dq->tail] = value;\n") - set ok to append_list(lines and " dq->tail = (dq->tail + 1) % dq->capacity;\n") - set ok to append_list(lines and " dq->size++;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long deque_push_front(long long dq_ptr, long long value) {\n") - set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") - set ok to append_list(lines and " if (!dq) return 0;\n") - set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n") - set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;\n") - set ok to append_list(lines and " dq->data[dq->head] = value;\n") - set ok to append_list(lines and " dq->size++;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long deque_pop_back(long long dq_ptr) {\n") - set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") - set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n") - set ok to append_list(lines and " dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;\n") - set ok to append_list(lines and " long long value = dq->data[dq->tail];\n") - set ok to append_list(lines and " dq->size--;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long deque_pop_front(long long dq_ptr) {\n") - set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") - set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n") - set ok to append_list(lines and " long long value = dq->data[dq->head];\n") - set ok to append_list(lines and " dq->head = (dq->head + 1) % dq->capacity;\n") - set ok to append_list(lines and " dq->size--;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long deque_length(long long dq_ptr) {\n") - set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") - set ok to append_list(lines and " if (!dq) return 0;\n") - set ok to append_list(lines and " return dq->size;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long free_deque(long long dq_ptr) {\n") - set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") - set ok to append_list(lines and " if (!dq) return 0;\n") - set ok to append_list(lines and " free(dq->data);\n") - set ok to append_list(lines and " free(dq);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Filesystem Operations */\n") - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "#include \n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_scan_dir(long long path_val) {\n") - set ok to append_list(lines and " const char* path = (const char*)path_val;\n") - set ok to append_list(lines and " long long list_ptr = create_list();\n") - set ok to append_list(lines and " if (!path) return list_ptr;\n") - set ok to append_list(lines and " DIR* d = opendir(path);\n") - set ok to append_list(lines and " if (!d) return list_ptr;\n") - set ok to append_list(lines and " struct dirent* dir;\n") - set ok to append_list(lines and " while ((dir = readdir(d)) != NULL) {\n") - set ok to append_list(lines and " if (strcmp(dir->d_name, \".\") == 0 || strcmp(dir->d_name, \"..\") == 0) {\n") - set ok to append_list(lines and " continue;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " char* name = strdup(dir->d_name);\n") - set ok to append_list(lines and " append_list(list_ptr, (long long)name);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " closedir(d);\n") - set ok to append_list(lines and " return list_ptr;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_copy_file(long long src_val, long long dest_val) {\n") - set ok to append_list(lines and " const char* src = (const char*)src_val;\n") - set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n") - set ok to append_list(lines and " if (!src || !dest) return 0;\n") - set ok to append_list(lines and " FILE* f_src = fopen(src, \"rb\");\n") - set ok to append_list(lines and " if (!f_src) return 0;\n") - set ok to append_list(lines and " FILE* f_dest = fopen(dest, \"wb\");\n") - set ok to append_list(lines and " if (!f_dest) {\n") - set ok to append_list(lines and " fclose(f_src);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " char buf[4096];\n") - set ok to append_list(lines and " size_t n;\n") - set ok to append_list(lines and " while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) {\n") - set ok to append_list(lines and " fwrite(buf, 1, n, f_dest);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " fclose(f_src);\n") - set ok to append_list(lines and " fclose(f_dest);\n") - set ok to append_list(lines and " return 1;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_delete_file(long long path_val) {\n") - set ok to append_list(lines and " const char* path = (const char*)path_val;\n") - set ok to append_list(lines and " if (!path) return 0;\n") - set ok to append_list(lines and " return remove(path) == 0 ? 1 : 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_move_file(long long src_val, long long dest_val) {\n") - set ok to append_list(lines and " const char* src = (const char*)src_val;\n") - set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n") - set ok to append_list(lines and " if (!src || !dest) return 0;\n") - set ok to append_list(lines and " return rename(src, dest) == 0 ? 1 : 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_exists(long long path_val) {\n") - set ok to append_list(lines and " const char* path = (const char*)path_val;\n") - set ok to append_list(lines and " if (!path) return 0;\n") - set ok to append_list(lines and " struct stat st;\n") - set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_is_dir(long long path_val) {\n") - set ok to append_list(lines and " const char* path = (const char*)path_val;\n") - set ok to append_list(lines and " if (!path) return 0;\n") - set ok to append_list(lines and " struct stat st;\n") - set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") - set ok to append_list(lines and " return S_ISDIR(st.st_mode) ? 1 : 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_is_file(long long path_val) {\n") - set ok to append_list(lines and " const char* path = (const char*)path_val;\n") - set ok to append_list(lines and " if (!path) return 0;\n") - set ok to append_list(lines and " struct stat st;\n") - set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") - set ok to append_list(lines and " return S_ISREG(st.st_mode) ? 1 : 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long fs_get_size(long long path_val) {\n") - set ok to append_list(lines and " const char* path = (const char*)path_val;\n") - set ok to append_list(lines and " if (!path) return 0;\n") - set ok to append_list(lines and " struct stat st;\n") - set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") - set ok to append_list(lines and " return (long long)st.st_size;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* HTTP Client */\n") - set ok to append_list(lines and "long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n") - set ok to append_list(lines and " const char* method = (const char*)method_val;\n") - set ok to append_list(lines and " const char* url = (const char*)url_val;\n") - set ok to append_list(lines and " const char* headers = (const char*)headers_val;\n") - set ok to append_list(lines and " const char* body = (const char*)body_val;\n") - set ok to append_list(lines and " if (!method || !url) return (long long)strdup(\"\");\n") - set ok to append_list(lines and " if (strncmp(url, \"http://\", 7) != 0) {\n") - set ok to append_list(lines and " return (long long)strdup(\"Error: only http:// protocol supported\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " const char* host_start = url + 7;\n") - set ok to append_list(lines and " const char* path_start = strchr(host_start, '/');\n") - set ok to append_list(lines and " char host[256];\n") - set ok to append_list(lines and " char path[1024];\n") - set ok to append_list(lines and " if (path_start) {\n") - set ok to append_list(lines and " size_t host_len = path_start - host_start;\n") - set ok to append_list(lines and " if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;\n") - set ok to append_list(lines and " strncpy(host, host_start, host_len);\n") - set ok to append_list(lines and " host[host_len] = '\\0';\n") - set ok to append_list(lines and " strncpy(path, path_start, sizeof(path) - 1);\n") - set ok to append_list(lines and " path[sizeof(path) - 1] = '\\0';\n") - set ok to append_list(lines and " } else {\n") - set ok to append_list(lines and " strncpy(host, host_start, sizeof(host) - 1);\n") - set ok to append_list(lines and " host[sizeof(host) - 1] = '\\0';\n") - set ok to append_list(lines and " strcpy(path, \"/\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " int port = 80;\n") - set ok to append_list(lines and " char* colon = strchr(host, ':');\n") - set ok to append_list(lines and " if (colon) {\n") - set ok to append_list(lines and " *colon = '\\0';\n") - set ok to append_list(lines and " port = atoi(colon + 1);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n") - set ok to append_list(lines and " if (sockfd < 0) return (long long)strdup(\"Error: socket creation failed\");\n") - set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n") - set ok to append_list(lines and " if (!server) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return (long long)strdup(\"Error: host resolution failed\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") - set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") - set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") - set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n") - set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") - set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return (long long)strdup(\"Error: connection failed\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " char req[4096];\n") - set ok to append_list(lines and " size_t body_len = body ? strlen(body) : 0;\n") - set ok to append_list(lines and " int req_len = snprintf(req, sizeof(req),\n") - set ok to append_list(lines and " \"%s %s HTTP/1.1\\r\\n\"\n") - set ok to append_list(lines and " \"Host: %s\\r\\n\"\n") - set ok to append_list(lines and " \"Content-Length: %zu\\r\\n\"\n") - set ok to append_list(lines and " \"Connection: close\\r\\n\"\n") - set ok to append_list(lines and " \"%s%s\"\n") - set ok to append_list(lines and " \"\\r\\n\",\n") - set ok to append_list(lines and " method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n") - set ok to append_list(lines and " if (send(sockfd, req, req_len, 0) < 0) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return (long long)strdup(\"Error: send failed\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " if (body_len > 0) {\n") - set ok to append_list(lines and " if (send(sockfd, body, body_len, 0) < 0) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return (long long)strdup(\"Error: send body failed\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " size_t resp_cap = 4096;\n") - set ok to append_list(lines and " size_t resp_len = 0;\n") - set ok to append_list(lines and " char* resp = malloc(resp_cap);\n") - set ok to append_list(lines and " if (!resp) {\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return (long long)strdup(\"\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " char recv_buf[4096];\n") - set ok to append_list(lines and " ssize_t n;\n") - set ok to append_list(lines and " while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {\n") - set ok to append_list(lines and " if (resp_len + n >= resp_cap) {\n") - set ok to append_list(lines and " resp_cap *= 2;\n") - set ok to append_list(lines and " char* new_resp = realloc(resp, resp_cap);\n") - set ok to append_list(lines and " if (!new_resp) {\n") - set ok to append_list(lines and " free(resp);\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return (long long)strdup(\"Error: memory allocation failed\");\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " resp = new_resp;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " memcpy(resp + resp_len, recv_buf, n);\n") - set ok to append_list(lines and " resp_len += n;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " resp[resp_len] = '\\0';\n") - set ok to append_list(lines and " close(sockfd);\n") - set ok to append_list(lines and " return (long long)resp;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))\n") - set ok to append_list(lines and "#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n") - set ok to append_list(lines and "#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n") - set ok to append_list(lines and "#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n") - set ok to append_list(lines and "#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n") - set ok to append_list(lines and "#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n") - set ok to append_list(lines and "#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " unsigned char data[64];\n") - set ok to append_list(lines and " unsigned int datalen;\n") - set ok to append_list(lines and " unsigned long long bitlen;\n") - set ok to append_list(lines and " unsigned int state[8];\n") - set ok to append_list(lines and "} EP_SHA256_CTX;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static const unsigned int sha256_k[64] = {\n") - set ok to append_list(lines and " 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n") - set ok to append_list(lines and " 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n") - set ok to append_list(lines and " 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n") - set ok to append_list(lines and " 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n") - set ok to append_list(lines and " 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n") - set ok to append_list(lines and " 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n") - set ok to append_list(lines and " 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n") - set ok to append_list(lines and " 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n") - set ok to append_list(lines and "};\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {\n") - set ok to append_list(lines and " unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n") - set ok to append_list(lines and " for (i = 0, j = 0; i < 16; ++i, j += 4)\n") - set ok to append_list(lines and " m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n") - set ok to append_list(lines and " for ( ; i < 64; ++i)\n") - set ok to append_list(lines and " m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n") - set ok to append_list(lines and " a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];\n") - set ok to append_list(lines and " e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];\n") - set ok to append_list(lines and " for (i = 0; i < 64; ++i) {\n") - set ok to append_list(lines and " t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];\n") - set ok to append_list(lines and " t2 = EP0(a) + MAJ(a,b,c);\n") - set ok to append_list(lines and " h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;\n") - set ok to append_list(lines and " ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_sha256_init(EP_SHA256_CTX *ctx) {\n") - set ok to append_list(lines and " ctx->datalen = 0; ctx->bitlen = 0;\n") - set ok to append_list(lines and " ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;\n") - set ok to append_list(lines and " ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {\n") - set ok to append_list(lines and " for (size_t i = 0; i < len; ++i) {\n") - set ok to append_list(lines and " ctx->data[ctx->datalen] = data[i];\n") - set ok to append_list(lines and " ctx->datalen++;\n") - set ok to append_list(lines and " if (ctx->datalen == 64) {\n") - set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n") - set ok to append_list(lines and " ctx->bitlen += 512;\n") - set ok to append_list(lines and " ctx->datalen = 0;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {\n") - set ok to append_list(lines and " unsigned int i = ctx->datalen;\n") - set ok to append_list(lines and " if (ctx->datalen < 56) {\n") - set ok to append_list(lines and " ctx->data[i++] = 0x80;\n") - set ok to append_list(lines and " while (i < 56) ctx->data[i++] = 0x00;\n") - set ok to append_list(lines and " } else {\n") - set ok to append_list(lines and " ctx->data[i++] = 0x80;\n") - set ok to append_list(lines and " while (i < 64) ctx->data[i++] = 0x00;\n") - set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n") - set ok to append_list(lines and " memset(ctx->data, 0, 56);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " ctx->bitlen += ctx->datalen * 8;\n") - set ok to append_list(lines and " ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;\n") - set ok to append_list(lines and " ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;\n") - set ok to append_list(lines and " ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;\n") - set ok to append_list(lines and " ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;\n") - set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n") - set ok to append_list(lines and " for (i = 0; i < 4; ++i) {\n") - set ok to append_list(lines and " hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "char* ep_sha256(const char* s) {\n") - set ok to append_list(lines and " if (!s) s = \"\";\n") - set ok to append_list(lines and " EP_SHA256_CTX ctx;\n") - set ok to append_list(lines and " ep_sha256_init(&ctx);\n") - set ok to append_list(lines and " ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));\n") - set ok to append_list(lines and " unsigned char hash[32];\n") - set ok to append_list(lines and " ep_sha256_final(&ctx, hash);\n") - set ok to append_list(lines and " char* result = malloc(65);\n") - set ok to append_list(lines and " if (result) {\n") - set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n") - set ok to append_list(lines and " sprintf(result + (i * 2), \"%02x\", hash[i]);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " result[64] = '\\0';\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return result;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "typedef struct {\n") - set ok to append_list(lines and " unsigned int count[2];\n") - set ok to append_list(lines and " unsigned int state[4];\n") - set ok to append_list(lines and " unsigned char buffer[64];\n") - set ok to append_list(lines and "} EP_MD5_CTX;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))\n") - set ok to append_list(lines and "#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))\n") - set ok to append_list(lines and "#define H(x,y,z) ((x) ^ (y) ^ (z))\n") - set ok to append_list(lines and "#define I(x,y,z) ((y) ^ ((x) | ~(z)))\n") - set ok to append_list(lines and "#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "#define FF(a,b,c,d,x,s,ac) { \\\n") - set ok to append_list(lines and " (a) += F((b),(c),(d)) + (x) + (ac); \\\n") - set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") - set ok to append_list(lines and " (a) += (b); \\\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "#define GG(a,b,c,d,x,s,ac) { \\\n") - set ok to append_list(lines and " (a) += G((b),(c),(d)) + (x) + (ac); \\\n") - set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") - set ok to append_list(lines and " (a) += (b); \\\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "#define HH(a,b,c,d,x,s,ac) { \\\n") - set ok to append_list(lines and " (a) += H((b),(c),(d)) + (x) + (ac); \\\n") - set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") - set ok to append_list(lines and " (a) += (b); \\\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "#define II(a,b,c,d,x,s,ac) { \\\n") - set ok to append_list(lines and " (a) += I((b),(c),(d)) + (x) + (ac); \\\n") - set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") - set ok to append_list(lines and " (a) += (b); \\\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_md5_init(EP_MD5_CTX *ctx) {\n") - set ok to append_list(lines and " ctx->count[0] = ctx->count[1] = 0;\n") - set ok to append_list(lines and " ctx->state[0] = 0x67452301;\n") - set ok to append_list(lines and " ctx->state[1] = 0xefcdab89;\n") - set ok to append_list(lines and " ctx->state[2] = 0x98badcfe;\n") - set ok to append_list(lines and " ctx->state[3] = 0x10325476;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {\n") - set ok to append_list(lines and " unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n") - set ok to append_list(lines and " for (int i = 0, j = 0; i < 16; i++, j += 4)\n") - set ok to append_list(lines and " x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);\n") - set ok to append_list(lines and " FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);\n") - set ok to append_list(lines and " FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);\n") - set ok to append_list(lines and " FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);\n") - set ok to append_list(lines and " GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);\n") - set ok to append_list(lines and " GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);\n") - set ok to append_list(lines and " GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);\n") - set ok to append_list(lines and " HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);\n") - set ok to append_list(lines and " HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);\n") - set ok to append_list(lines and " HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);\n") - set ok to append_list(lines and " II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);\n") - set ok to append_list(lines and " II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);\n") - set ok to append_list(lines and " II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and " state[0] += a; state[1] += b; state[2] += c; state[3] += d;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {\n") - set ok to append_list(lines and " unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;\n") - set ok to append_list(lines and " ctx->count[0] += input_len << 3;\n") - set ok to append_list(lines and " if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;\n") - set ok to append_list(lines and " ctx->count[1] += input_len >> 29;\n") - set ok to append_list(lines and " if (input_len >= part_len) {\n") - set ok to append_list(lines and " memcpy(&ctx->buffer[index], input, part_len);\n") - set ok to append_list(lines and " ep_md5_transform(ctx->state, ctx->buffer);\n") - set ok to append_list(lines and " for (i = part_len; i + 63 < input_len; i += 64)\n") - set ok to append_list(lines and " ep_md5_transform(ctx->state, &input[i]);\n") - set ok to append_list(lines and " index = 0;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " memcpy(&ctx->buffer[index], &input[i], input_len - i);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n") - set ok to append_list(lines and " unsigned char bits[8];\n") - set ok to append_list(lines and " bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n") - set ok to append_list(lines and " bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n") - set ok to append_list(lines and " unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n") - set ok to append_list(lines and " unsigned char padding[64];\n") - set ok to append_list(lines and " memset(padding, 0, 64); padding[0] = 0x80;\n") - set ok to append_list(lines and " ep_md5_update(ctx, padding, pad_len);\n") - set ok to append_list(lines and " ep_md5_update(ctx, bits, 8);\n") - set ok to append_list(lines and " for (int i = 0; i < 4; i++) {\n") - set ok to append_list(lines and " digest[i*4] = ctx->state[i];\n") - set ok to append_list(lines and " digest[i*4 + 1] = ctx->state[i] >> 8;\n") - set ok to append_list(lines and " digest[i*4 + 2] = ctx->state[i] >> 16;\n") - set ok to append_list(lines and " digest[i*4 + 3] = ctx->state[i] >> 24;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "char* ep_md5(const char* s) {\n") - set ok to append_list(lines and " if (!s) s = \"\";\n") - set ok to append_list(lines and " EP_MD5_CTX ctx;\n") - set ok to append_list(lines and " ep_md5_init(&ctx);\n") - set ok to append_list(lines and " ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));\n") - set ok to append_list(lines and " unsigned char hash[16];\n") - set ok to append_list(lines and " ep_md5_final(&ctx, hash);\n") - set ok to append_list(lines and " char* result = malloc(33);\n") - set ok to append_list(lines and " if (result) {\n") - set ok to append_list(lines and " for (int i = 0; i < 16; i++) {\n") - set ok to append_list(lines and " sprintf(result + (i * 2), \"%02x\", hash[i]);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " result[32] = '\\0';\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return result;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "char* read_file_content(const char* filepath) {\n") - set ok to append_list(lines and " char mode[3];\n") - set ok to append_list(lines and " mode[0] = 'r';\n") - set ok to append_list(lines and " mode[1] = 'b';\n") - set ok to append_list(lines and " mode[2] = '\\0';\n") - set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n") - set ok to append_list(lines and " if (!f) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n") - set ok to append_list(lines and " long size = ftell(f);\n") - set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n") - set ok to append_list(lines and " char* buf = malloc(size + 1);\n") - set ok to append_list(lines and " if (!buf) {\n") - set ok to append_list(lines and " fclose(f);\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " size_t read_bytes = fread(buf, 1, size, f);\n") - set ok to append_list(lines and " buf[read_bytes] = '\\0';\n") - set ok to append_list(lines and " fclose(f);\n") - set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return buf;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long string_length(const char* s) {\n") - set ok to append_list(lines and " if (!s) return 0;\n") - set ok to append_list(lines and " return strlen(s);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long get_character(const char* s, long long index) {\n") - set ok to append_list(lines and " if (!s) return 0;\n") - set ok to append_list(lines and " long long len = strlen(s);\n") - set ok to append_list(lines and " if (index < 0 || index >= len) return 0;\n") - set ok to append_list(lines and " return (unsigned char)s[index];\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long create_list(void) {\n") - set ok to append_list(lines and " EpList* list = malloc(sizeof(EpList));\n") - set ok to append_list(lines and " if (!list) return 0;\n") - set ok to append_list(lines and " list->capacity = 4;\n") - set ok to append_list(lines and " list->length = 0;\n") - set ok to append_list(lines and " list->data = malloc(list->capacity * sizeof(long long));\n") - set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n") - set ok to append_list(lines and " return (long long)list;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long get_list_data_ptr(long long list_ptr) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list) return 0;\n") - set ok to append_list(lines and " return (long long)list->data;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long append_list(long long list_ptr, long long value) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list) return 0;\n") - set ok to append_list(lines and " if (list->length >= list->capacity) {\n") - set ok to append_list(lines and " list->capacity *= 2;\n") - set ok to append_list(lines and " list->data = realloc(list->data, list->capacity * sizeof(long long));\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " list->data[list->length] = value;\n") - set ok to append_list(lines and " list->length += 1;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long get_list(long long list_ptr, long long index) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list || index < 0 || index >= list->length) return 0;\n") - set ok to append_list(lines and " return list->data[index];\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long set_list(long long list_ptr, long long index, long long value) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list || index < 0 || index >= list->length) return 0;\n") - set ok to append_list(lines and " list->data[index] = value;\n") - set ok to append_list(lines and " return value;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long length_list(long long list_ptr) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list) return 0;\n") - set ok to append_list(lines and " return list->length;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long free_list(long long list_ptr) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list) return 0;\n") - set ok to append_list(lines and " ep_gc_unregister(list);\n") - set ok to append_list(lines and " free(list->data);\n") - set ok to append_list(lines and " free(list);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {\n") - set ok to append_list(lines and " EpList* rows = (EpList*)arg;\n") - set ok to append_list(lines and " EpList* row = (EpList*)create_list();\n") - set ok to append_list(lines and " for (int i = 0; i < argc; i++) {\n") - set ok to append_list(lines and " char* val = argv[i] ? strdup(argv[i]) : strdup(\"\");\n") - set ok to append_list(lines and " append_list((long long)row, (long long)val);\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " append_list((long long)rows, (long long)row);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long sqlite_get_callback_ptr(long long dummy) {\n") - set ok to append_list(lines and " return (long long)sqlite_list_callback;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "int ep_argc = 0;\n") - set ok to append_list(lines and "char** ep_argv = NULL;\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "void init_ep_args(int argc, char** argv) {\n") - set ok to append_list(lines and " ep_argc = argc;\n") - set ok to append_list(lines and " ep_argv = argv;\n") - set ok to append_list(lines and " ep_gc_register_thread((void*)&argc);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long get_argument_count(void) {\n") - set ok to append_list(lines and " return ep_argc;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "const char* get_argument(long long index) {\n") - set ok to append_list(lines and " if (index < 0 || index >= ep_argc) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " return ep_argv[index];\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long write_file_content(const char* filepath, const char* content) {\n") - set ok to append_list(lines and " char mode[3];\n") - set ok to append_list(lines and " mode[0] = 'w';\n") - set ok to append_list(lines and " mode[1] = 'b';\n") - set ok to append_list(lines and " mode[2] = '\\0';\n") - set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n") - set ok to append_list(lines and " if (!f) return 0;\n") - set ok to append_list(lines and " size_t len = strlen(content);\n") - set ok to append_list(lines and " size_t written = fwrite(content, 1, len, f);\n") - set ok to append_list(lines and " fclose(f);\n") - set ok to append_list(lines and " return written == len ? 1 : 0;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long run_command(const char* command) {\n") - set ok to append_list(lines and " if (!command) return -1;\n") - set ok to append_list(lines and " return system(command);\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "char* substring(const char* s, long long start, long long len) {\n") - set ok to append_list(lines and " if (!s) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " long long total_len = strlen(s);\n") - set ok to append_list(lines and " if (start < 0 || start >= total_len || len <= 0) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " if (start + len > total_len) {\n") - set ok to append_list(lines and " len = total_len - start;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " char* sub = malloc(len + 1);\n") - set ok to append_list(lines and " if (!sub) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " strncpy(sub, s + start, len);\n") - set ok to append_list(lines and " sub[len] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(sub, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return sub;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "char* string_from_list(long long list_ptr) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " char* s = malloc(list->length + 1);\n") - set ok to append_list(lines and " if (!s) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return empty;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") - set ok to append_list(lines and " s[i] = (char)list->data[i];\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " s[list->length] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(s, EP_OBJ_STRING);\n") - set ok to append_list(lines and " return s;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long pop_list(long long list_ptr) {\n") - set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list || list->length <= 0) return 0;\n") - set ok to append_list(lines and " list->length -= 1;\n") - set ok to append_list(lines and " return list->data[list->length];\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long display_string(const char* s) {\n") - set ok to append_list(lines and " if (s) puts(s);\n") - set ok to append_list(lines and " return 0;\n") - set ok to append_list(lines and "}\n") - return join_strings(lines) + # The C runtime is shared with the Rust compiler: single source of truth + # in runtime/ep_runtime.c + runtime/ep_builtins.c, embedded here via the + # generated module ep_runtime_gen.ep (regenerate with tools/gen_runtime_ep.ep). + return get_shared_runtime_source() define get_c_main_source: set lines to create_list() @@ -2987,7 +1530,7 @@ define get_c_test_main_source with program: set ok to append_list(lines and " init_ep_args(argc, argv);\n") set ok to append_list(lines and " printf(\"Running ") - set ok to append_list(lines and ep_int_to_str(test_count)) + set ok to append_list(lines and cg_int_to_str(test_count)) set ok to append_list(lines and " tests...\\n\");\n") set ok to append_list(lines and " int passed = 0;\n") @@ -3531,13 +2074,13 @@ define generate_c with program and is_test_mode: set line to "#define EP_FIELD_" set line to string_concat(line and fname) set line to string_concat(line and " ") - set line to string_concat(line and ep_int_to_str(fs_idx)) + set line to string_concat(line and cg_int_to_str(fs_idx)) set line to string_concat(line and "\n") set ok to emit(state and line) set fs_idx to fs_idx + 1 # Every struct instance is allocated with room for any global field index. set slot_line to "#define EP_STRUCT_MAX_SLOTS " - set slot_line to string_concat(slot_line and ep_int_to_str(length_list(field_slots) + 8)) + set slot_line to string_concat(slot_line and cg_int_to_str(length_list(field_slots) + 8)) set slot_line to string_concat(slot_line and "\n") set ok to emit(state and slot_line) @@ -3572,7 +2115,7 @@ define generate_c with program and is_test_mode: set line to "#define EP_TAG_" set line to string_concat(line and tname) set line to string_concat(line and " ") - set line to string_concat(line and ep_int_to_str(ts_idx)) + set line to string_concat(line and cg_int_to_str(ts_idx)) set line to string_concat(line and "\n") set ok to emit(state and line) set ts_idx to ts_idx + 1 @@ -3670,23 +2213,23 @@ define generate_c with program and is_test_mode: set j to 0 repeat while j < args_len: set struct_decl to string_concat(struct_decl and " long long arg") - set struct_decl to string_concat(struct_decl and ep_int_to_str(j)) + set struct_decl to string_concat(struct_decl and cg_int_to_str(j)) set struct_decl to string_concat(struct_decl and ";\n") set j to j + 1 if args_len == 0: set struct_decl to string_concat(struct_decl and " int dummy;\n") set struct_decl to string_concat(struct_decl and "} spawn_args_") - set struct_decl to string_concat(struct_decl and ep_int_to_str(idx)) + set struct_decl to string_concat(struct_decl and cg_int_to_str(idx)) set struct_decl to string_concat(struct_decl and ";\n\n") set ok to emit(state and struct_decl) set wrap_fn to "void* spawn_wrapper_" - set wrap_fn to string_concat(wrap_fn and ep_int_to_str(idx)) + set wrap_fn to string_concat(wrap_fn and cg_int_to_str(idx)) set wrap_fn to string_concat(wrap_fn and "(void* r) {\n") set wrap_fn to string_concat(wrap_fn and " spawn_args_") - set wrap_fn to string_concat(wrap_fn and ep_int_to_str(idx)) + set wrap_fn to string_concat(wrap_fn and cg_int_to_str(idx)) set wrap_fn to string_concat(wrap_fn and "* args = (spawn_args_") - set wrap_fn to string_concat(wrap_fn and ep_int_to_str(idx)) + set wrap_fn to string_concat(wrap_fn and cg_int_to_str(idx)) set wrap_fn to string_concat(wrap_fn and "*)r;\n") set wrap_fn to string_concat(wrap_fn and " ") set c_name to func_name @@ -3697,7 +2240,7 @@ define generate_c with program and is_test_mode: set j to 0 repeat while j < args_len: set wrap_fn to string_concat(wrap_fn and "args->arg") - set wrap_fn to string_concat(wrap_fn and ep_int_to_str(j)) + set wrap_fn to string_concat(wrap_fn and cg_int_to_str(j)) if j < args_len - 1: set wrap_fn to string_concat(wrap_fn and ", ") set j to j + 1 diff --git a/ep_runtime_gen.ep b/ep_runtime_gen.ep new file mode 100644 index 0000000..339b091 --- /dev/null +++ b/ep_runtime_gen.ep @@ -0,0 +1,5055 @@ +# GENERATED FILE - DO NOT EDIT. +# Source of truth: runtime/ep_runtime.c + runtime/ep_builtins.c +# Regenerate: ./target/release/ernos tools/gen_runtime_ep.ep && ./tools/gen_runtime_ep + +define ep_rt_core_0: + set lines to create_list() + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and "#define _SETJMP_H\n") + set ok to append_list(lines and "typedef int jmp_buf[1];\n") + set ok to append_list(lines and "#define setjmp(buf) (0)\n") + set ok to append_list(lines and "#define longjmp(buf, val) abort()\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// Mock pthreads for single-threaded WASM\n") + set ok to append_list(lines and "typedef struct { int lock_state; } pthread_mutex_t;\n") + set ok to append_list(lines and "typedef struct { int cond_state; } pthread_cond_t;\n") + set ok to append_list(lines and "typedef struct { int rw_state; } pthread_rwlock_t;\n") + set ok to append_list(lines and "typedef int pthread_t;\n") + set ok to append_list(lines and "typedef int pthread_attr_t;\n") + set ok to append_list(lines and "#define PTHREAD_MUTEX_INITIALIZER {0}\n") + set ok to append_list(lines and "#define PTHREAD_COND_INITIALIZER {0}\n") + set ok to append_list(lines and "#define PTHREAD_RWLOCK_INITIALIZER {0}\n") + set ok to append_list(lines and "#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0)\n") + set ok to append_list(lines and "#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0)\n") + set ok to append_list(lines and "#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0)\n") + set ok to append_list(lines and "#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1)\n") + set ok to append_list(lines and "#define pthread_mutex_destroy(m) ((void)(m), 0)\n") + set ok to append_list(lines and "#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0)\n") + set ok to append_list(lines and "#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0)\n") + set ok to append_list(lines and "#define pthread_cond_signal(c) ((void)(c), 0)\n") + set ok to append_list(lines and "#define pthread_cond_broadcast(c) ((void)(c), 0)\n") + set ok to append_list(lines and "#define pthread_cond_destroy(c) ((void)(c), 0)\n") + set ok to append_list(lines and "#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0)\n") + set ok to append_list(lines and "#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0)\n") + set ok to append_list(lines and "#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0)\n") + set ok to append_list(lines and "#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0)\n") + set ok to append_list(lines and "#define pthread_rwlock_destroy(r) ((void)(r), 0)\n") + set ok to append_list(lines and "#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0)\n") + set ok to append_list(lines and "#define pthread_join(t, r) ((void)(t), (void)(r), 0)\n") + set ok to append_list(lines and "#define pthread_detach(t) ((void)(t), 0)\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#ifndef _WIN32\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#if defined(__APPLE__)\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#if defined(__linux__)\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on\n") + set ok to append_list(lines and " Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a\n") + set ok to append_list(lines and " /dev/urandom read elsewhere. Only if all of those are unavailable does it\n") + set ok to append_list(lines and " fall back to rand() — never on a supported platform. */\n") + set ok to append_list(lines and "static void ep_secure_random_bytes(unsigned char* buf, size_t n) {\n") + set ok to append_list(lines and "#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)\n") + set ok to append_list(lines and " arc4random_buf(buf, n);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " size_t got = 0;\n") + set ok to append_list(lines and " #if defined(__linux__)\n") + set ok to append_list(lines and " while (got < n) {\n") + set ok to append_list(lines and " ssize_t r = getrandom(buf + got, n - got, 0);\n") + set ok to append_list(lines and " if (r <= 0) break;\n") + set ok to append_list(lines and " got += (size_t)r;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " #endif\n") + set ok to append_list(lines and " if (got < n) {\n") + set ok to append_list(lines and " FILE* f = fopen(\"/dev/urandom\", \"rb\");\n") + set ok to append_list(lines and " if (f) {\n") + set ok to append_list(lines and " got += fread(buf + got, 1, n - got, f);\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " while (got < n) {\n") + set ok to append_list(lines and " buf[got++] = (unsigned char)(rand() & 0xFF);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Try/catch infrastructure */\n") + set ok to append_list(lines and "static jmp_buf ep_try_buf;\n") + set ok to append_list(lines and "static volatile int ep_try_active = 0;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_signal_handler(int sig) {\n") + set ok to append_list(lines and " if (ep_try_active) {\n") + set ok to append_list(lines and " ep_try_active = 0;\n") + set ok to append_list(lines and " longjmp(ep_try_buf, sig);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Outside try: print error and exit */\n") + set ok to append_list(lines and " const char* name = sig == SIGSEGV ? \"segmentation fault (null pointer or invalid memory access)\"\n") + set ok to append_list(lines and " : sig == SIGFPE ? \"arithmetic error (division by zero)\"\n") + set ok to append_list(lines and " : sig == SIGABRT ? \"aborted\"\n") + set ok to append_list(lines and " : \"unknown signal\";\n") + set ok to append_list(lines and " fprintf(stderr, \"\\nRuntime Error: %s (signal %d)\\n\", name, sig);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " /* Write to daemon/general log file if environment variable is set */\n") + set ok to append_list(lines and " const char* daemon_log = getenv(\"ERNOS_DAEMON_LOG\");\n") + set ok to append_list(lines and " if (!daemon_log || daemon_log[0] == '\\0') {\n") + set ok to append_list(lines and " daemon_log = getenv(\"ERNOS_LOG_FILE\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (daemon_log && daemon_log[0] != '\\0') {\n") + set ok to append_list(lines and " FILE* f = fopen(daemon_log, \"ab\");\n") + set ok to append_list(lines and " if (f) {\n") + set ok to append_list(lines and " time_t rawtime;\n") + set ok to append_list(lines and " time(&rawtime);\n") + set ok to append_list(lines and " struct tm * timeinfo = localtime(&rawtime);\n") + set ok to append_list(lines and " char time_buf[80];\n") + set ok to append_list(lines and " if (timeinfo) {\n") + set ok to append_list(lines and " strftime(time_buf, sizeof(time_buf), \"%Y-%m-%d %H:%M:%S\", timeinfo);\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " snprintf(time_buf, sizeof(time_buf), \"%lld\", (long long)rawtime);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " fprintf(f, \"[%s] FATAL: Runtime Error: %s (signal %d)\\n\", time_buf, name, sig);\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " _exit(128 + sig);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#ifdef _MSC_VER\n") + set ok to append_list(lines and "static void ep_install_signal_handlers(void);\n") + set ok to append_list(lines and "#pragma section(\".CRT$XCU\", read)\n") + set ok to append_list(lines and "__declspec(allocate(\".CRT$XCU\")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers;\n") + set ok to append_list(lines and "static void ep_install_signal_handlers(void) {\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "__attribute__((constructor))\n") + set ok to append_list(lines and "static void ep_install_signal_handlers(void) {\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " signal(SIGFPE, ep_signal_handler);\n") + set ok to append_list(lines and " signal(SIGSEGV, ep_signal_handler);\n") + set ok to append_list(lines and " signal(SIGABRT, ep_signal_handler);\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#if defined(__wasm__)\n") + set ok to append_list(lines and " typedef int ep_thread_t;\n") + set ok to append_list(lines and " typedef int ep_mutex_t;\n") + set ok to append_list(lines and " typedef int ep_cond_t;\n") + set ok to append_list(lines and " #define ep_mutex_init(m) (void)(0)\n") + set ok to append_list(lines and " #define ep_mutex_lock(m) (void)(0)\n") + set ok to append_list(lines and " #define ep_mutex_unlock(m) (void)(0)\n") + set ok to append_list(lines and " #define ep_cond_init(c) (void)(0)\n") + set ok to append_list(lines and " #define ep_cond_wait(c, m) (void)(0)\n") + set ok to append_list(lines and " #define ep_cond_signal(c) (void)(0)\n") + return join_strings(lines) + +define ep_rt_core_1: + set lines to create_list() + set ok to append_list(lines and "#elif defined(_WIN32)\n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #pragma comment(lib, \"ws2_32.lib\")\n") + set ok to append_list(lines and " typedef HANDLE ep_thread_t;\n") + set ok to append_list(lines and " typedef CRITICAL_SECTION ep_mutex_t;\n") + set ok to append_list(lines and " typedef CONDITION_VARIABLE ep_cond_t;\n") + set ok to append_list(lines and " #define ep_mutex_init(m) InitializeCriticalSection(m)\n") + set ok to append_list(lines and " #define ep_mutex_lock(m) EnterCriticalSection(m)\n") + set ok to append_list(lines and " #define ep_mutex_unlock(m) LeaveCriticalSection(m)\n") + set ok to append_list(lines and " #define ep_cond_init(c) InitializeConditionVariable(c)\n") + set ok to append_list(lines and " #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)\n") + set ok to append_list(lines and " #define ep_cond_signal(c) WakeConditionVariable(c)\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " typedef pthread_t ep_thread_t;\n") + set ok to append_list(lines and " typedef pthread_mutex_t ep_mutex_t;\n") + set ok to append_list(lines and " typedef pthread_cond_t ep_cond_t;\n") + set ok to append_list(lines and " #define ep_mutex_init(m) pthread_mutex_init(m, NULL)\n") + set ok to append_list(lines and " #define ep_mutex_lock(m) pthread_mutex_lock(m)\n") + set ok to append_list(lines and " #define ep_mutex_unlock(m) pthread_mutex_unlock(m)\n") + set ok to append_list(lines and " #define ep_cond_init(c) pthread_cond_init(c, NULL)\n") + set ok to append_list(lines and " #define ep_cond_wait(c, m) pthread_cond_wait(c, m)\n") + set ok to append_list(lines and " #define ep_cond_signal(c) pthread_cond_signal(c)\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#if !defined(__wasm__) && !defined(_WIN32)\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef enum {\n") + set ok to append_list(lines and " EP_OBJ_LIST,\n") + set ok to append_list(lines and " EP_OBJ_STRING,\n") + set ok to append_list(lines and " EP_OBJ_STRUCT,\n") + set ok to append_list(lines and " EP_OBJ_CLOSURE,\n") + set ok to append_list(lines and " EP_OBJ_MAP\n") + set ok to append_list(lines and "} EpObjKind;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct EpGCObject {\n") + set ok to append_list(lines and " EpObjKind kind;\n") + set ok to append_list(lines and " int marked;\n") + set ok to append_list(lines and " void* ptr; /* actual allocation pointer */\n") + set ok to append_list(lines and " long long size; /* payload size for structs */\n") + set ok to append_list(lines and " long long num_fields; /* number of fields for structs (each is long long) */\n") + set ok to append_list(lines and " int generation; /* 0 = Nursery/young, 1 = Old */\n") + set ok to append_list(lines and " struct EpGCObject* next; /* intrusive linked list */\n") + set ok to append_list(lines and "} EpGCObject;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_now_ms(void);\n") + set ok to append_list(lines and "long long ep_sleep_ms(long long ms);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct EpTask EpTask;\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " long long chan;\n") + set ok to append_list(lines and " int completed;\n") + set ok to append_list(lines and " long long value;\n") + set ok to append_list(lines and " EpTask* waiting_task;\n") + set ok to append_list(lines and "} EpFuture;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long ep_await_future(EpFuture* fut);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "struct EpTask {\n") + set ok to append_list(lines and " long long (*step)(void*); /* pointer to step function */\n") + set ok to append_list(lines and " void* args; /* pointer to step state arguments */\n") + set ok to append_list(lines and " long long args_size_bytes; /* size of args struct for GC tracing */\n") + set ok to append_list(lines and " EpTask* next; /* run-queue link pointer */\n") + set ok to append_list(lines and " EpFuture* fut; /* future associated with this task */\n") + set ok to append_list(lines and " int state; /* coroutine execution state */\n") + set ok to append_list(lines and " int is_cancelled; /* cancellation flag for structured concurrency */\n") + set ok to append_list(lines and " struct EpTask* parent; /* parent task for structured concurrency cancellation */\n") + set ok to append_list(lines and "};\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Event Loop Scheduler Globals & Functions */\n") + set ok to append_list(lines and "static EpTask* ep_run_queue_head = NULL;\n") + set ok to append_list(lines and "static EpTask* ep_run_queue_tail = NULL;\n") + set ok to append_list(lines and "static EpTask* ep_current_task = NULL;\n") + set ok to append_list(lines and "static int ep_event_loop_fd = -1; /* epoll or kqueue fd */\n") + set ok to append_list(lines and "static int ep_active_io_sources = 0;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_task_enqueue(EpTask* task) {\n") + set ok to append_list(lines and " if (!task) return;\n") + set ok to append_list(lines and " task->next = NULL;\n") + set ok to append_list(lines and " if (ep_run_queue_tail) {\n") + set ok to append_list(lines and " ep_run_queue_tail->next = task;\n") + set ok to append_list(lines and " ep_run_queue_tail = task;\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " ep_run_queue_head = ep_run_queue_tail = task;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static EpTask* ep_task_dequeue(void) {\n") + set ok to append_list(lines and " if (!ep_run_queue_head) return NULL;\n") + set ok to append_list(lines and " EpTask* task = ep_run_queue_head;\n") + set ok to append_list(lines and " ep_run_queue_head = ep_run_queue_head->next;\n") + set ok to append_list(lines and " if (!ep_run_queue_head) ep_run_queue_tail = NULL;\n") + set ok to append_list(lines and " return task;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#ifndef __wasm__\n") + set ok to append_list(lines and "#ifdef __APPLE__\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_async_loop_init(void) {\n") + set ok to append_list(lines and " if (ep_event_loop_fd != -1) return;\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and " ep_event_loop_fd = 999;\n") + set ok to append_list(lines and "#elif defined(__APPLE__)\n") + set ok to append_list(lines and " ep_event_loop_fd = kqueue();\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " ep_event_loop_fd = epoll_create1(0);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct EpTimer {\n") + set ok to append_list(lines and " long long expiry_ms;\n") + set ok to append_list(lines and " EpTask* task;\n") + set ok to append_list(lines and " struct EpTimer* next;\n") + set ok to append_list(lines and "} EpTimer;\n") + set ok to append_list(lines and "static EpTimer* ep_timers_head = NULL;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_async_register_timer(long long timeout_ms, EpTask* task) {\n") + set ok to append_list(lines and " long long expiry = ep_time_now_ms() + timeout_ms;\n") + set ok to append_list(lines and " EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer));\n") + set ok to append_list(lines and " timer->expiry_ms = expiry;\n") + set ok to append_list(lines and " timer->task = task;\n") + set ok to append_list(lines and " timer->next = NULL;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " /* Insert sorted */\n") + set ok to append_list(lines and " if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) {\n") + set ok to append_list(lines and " timer->next = ep_timers_head;\n") + set ok to append_list(lines and " ep_timers_head = timer;\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " EpTimer* cur = ep_timers_head;\n") + set ok to append_list(lines and " while (cur->next && cur->next->expiry_ms <= expiry) {\n") + return join_strings(lines) + +define ep_rt_core_2: + set lines to create_list() + set ok to append_list(lines and " cur = cur->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " timer->next = cur->next;\n") + set ok to append_list(lines and " cur->next = timer;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long ep_get_next_timer_timeout(void) {\n") + set ok to append_list(lines and " if (!ep_timers_head) return -1; /* block indefinitely */\n") + set ok to append_list(lines and " long long now = ep_time_now_ms();\n") + set ok to append_list(lines and " long long diff = ep_timers_head->expiry_ms - now;\n") + set ok to append_list(lines and " return diff < 0 ? 0 : diff;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_process_expired_timers(void) {\n") + set ok to append_list(lines and " long long now = ep_time_now_ms();\n") + set ok to append_list(lines and " while (ep_timers_head && ep_timers_head->expiry_ms <= now) {\n") + set ok to append_list(lines and " EpTimer* expired = ep_timers_head;\n") + set ok to append_list(lines and " ep_timers_head = ep_timers_head->next;\n") + set ok to append_list(lines and " ep_task_enqueue(expired->task);\n") + set ok to append_list(lines and " free(expired);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_async_register_read(int fd, EpTask* task) {\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and " (void)fd;\n") + set ok to append_list(lines and " (void)task;\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " ep_async_loop_init();\n") + set ok to append_list(lines and " ep_active_io_sources++;\n") + set ok to append_list(lines and "#ifdef __APPLE__\n") + set ok to append_list(lines and " struct kevent ev;\n") + set ok to append_list(lines and " EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task);\n") + set ok to append_list(lines and " kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " struct epoll_event ev;\n") + set ok to append_list(lines and " ev.events = EPOLLIN | EPOLLONESHOT;\n") + set ok to append_list(lines and " ev.data.ptr = task;\n") + set ok to append_list(lines and " if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {\n") + set ok to append_list(lines and " epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_async_wait_step(long long timeout) {\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and " if (timeout > 0) {\n") + set ok to append_list(lines and " ep_sleep_ms(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "#ifdef __APPLE__\n") + set ok to append_list(lines and " struct kevent events[16];\n") + set ok to append_list(lines and " struct timespec ts;\n") + set ok to append_list(lines and " struct timespec* p_ts = NULL;\n") + set ok to append_list(lines and " if (timeout >= 0) {\n") + set ok to append_list(lines and " ts.tv_sec = timeout / 1000;\n") + set ok to append_list(lines and " ts.tv_nsec = (timeout % 1000) * 1000000;\n") + set ok to append_list(lines and " p_ts = &ts;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts);\n") + set ok to append_list(lines and " for (int i = 0; i < n; i++) {\n") + set ok to append_list(lines and " EpTask* t = (EpTask*)events[i].udata;\n") + set ok to append_list(lines and " ep_task_enqueue(t);\n") + set ok to append_list(lines and " ep_active_io_sources--;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " struct epoll_event events[16];\n") + set ok to append_list(lines and " int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout);\n") + set ok to append_list(lines and " for (int i = 0; i < n; i++) {\n") + set ok to append_list(lines and " EpTask* t = (EpTask*)events[i].data.ptr;\n") + set ok to append_list(lines and " ep_task_enqueue(t);\n") + set ok to append_list(lines and " ep_active_io_sources--;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " ep_process_expired_timers();\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_async_loop_run(void) {\n") + set ok to append_list(lines and " ep_async_loop_init();\n") + set ok to append_list(lines and " while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) {\n") + set ok to append_list(lines and " /* 1. Run all runnable tasks */\n") + set ok to append_list(lines and " while (ep_run_queue_head) {\n") + set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\n") + set ok to append_list(lines and " if (task->is_cancelled) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " task->fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " continue;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_current_task = task;\n") + set ok to append_list(lines and " long long res = task->step(task->args);\n") + set ok to append_list(lines and " ep_current_task = NULL;\n") + set ok to append_list(lines and " if (res != -999999) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->value = res;\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " if (task->fut->waiting_task) {\n") + set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n") + set ok to append_list(lines and " task->fut->waiting_task = NULL;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " /* 2. If no tasks runnable, wait for I/O / timers */\n") + set ok to append_list(lines and " if (!ep_run_queue_head) {\n") + set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n") + set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n") + set ok to append_list(lines and " break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n") + set ok to append_list(lines and " if (timeout > 0) {\n") + set ok to append_list(lines and " ep_sleep_ms(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_process_expired_timers();\n") + set ok to append_list(lines and " continue;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " ep_async_wait_step(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long ep_await_future(EpFuture* fut) {\n") + set ok to append_list(lines and " if (!fut) return 0;\n") + set ok to append_list(lines and " while (!fut->completed) {\n") + set ok to append_list(lines and " if (ep_run_queue_head) {\n") + set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\n") + set ok to append_list(lines and " if (task) {\n") + set ok to append_list(lines and " if (task->is_cancelled) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " task->fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " EpTask* saved_current = ep_current_task;\n") + set ok to append_list(lines and " ep_current_task = task;\n") + set ok to append_list(lines and " long long res = task->step(task->args);\n") + set ok to append_list(lines and " ep_current_task = saved_current;\n") + return join_strings(lines) + +define ep_rt_core_3: + set lines to create_list() + set ok to append_list(lines and " if (res != -999999) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->value = res;\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " if (task->fut->waiting_task) {\n") + set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n") + set ok to append_list(lines and " task->fut->waiting_task = NULL;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n") + set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n") + set ok to append_list(lines and " fprintf(stderr, \"Deadlock detected: awaiting incomplete future with no active tasks or timers.\\n\");\n") + set ok to append_list(lines and " exit(1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n") + set ok to append_list(lines and " if (timeout > 0) {\n") + set ok to append_list(lines and " ep_sleep_ms(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_process_expired_timers();\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " ep_async_wait_step(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return fut->value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind);\n") + set ok to append_list(lines and "long long create_list(void);\n") + set ok to append_list(lines and "long long append_list(long long list_ptr, long long value);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " EpFuture* futures[128];\n") + set ok to append_list(lines and " int count;\n") + set ok to append_list(lines and " int has_error;\n") + set ok to append_list(lines and "} EpTaskGroup;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " EpFuture* fut;\n") + set ok to append_list(lines and " int timer_fired;\n") + set ok to append_list(lines and "} EpTimeoutArgs;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static EpTask* ep_find_task_by_future(EpFuture* fut) {\n") + set ok to append_list(lines and " if (!fut) return NULL;\n") + set ok to append_list(lines and " EpTask* cur = ep_run_queue_head;\n") + set ok to append_list(lines and " while (cur) {\n") + set ok to append_list(lines and " if (cur->fut == fut) return cur;\n") + set ok to append_list(lines and " cur = cur->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n") + set ok to append_list(lines and " while (timer) {\n") + set ok to append_list(lines and " if (timer->task && timer->task->fut == fut) return timer->task;\n") + set ok to append_list(lines and " timer = timer->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return NULL;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_cancel_task(EpTask* task) {\n") + set ok to append_list(lines and " if (!task) return;\n") + set ok to append_list(lines and " task->is_cancelled = 1;\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " task->fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " // Cancel children in run queue\n") + set ok to append_list(lines and " EpTask* cur = ep_run_queue_head;\n") + set ok to append_list(lines and " while (cur) {\n") + set ok to append_list(lines and " if (cur->parent == task) {\n") + set ok to append_list(lines and " ep_cancel_task(cur);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " cur = cur->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " // Cancel children in timers queue\n") + set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n") + set ok to append_list(lines and " while (timer) {\n") + set ok to append_list(lines and " if (timer->task && timer->task->parent == task) {\n") + set ok to append_list(lines and " ep_cancel_task(timer->task);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " timer = timer->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long create_task_group(void) {\n") + set ok to append_list(lines and " EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup));\n") + set ok to append_list(lines and " tg->count = 0;\n") + set ok to append_list(lines and " tg->has_error = 0;\n") + set ok to append_list(lines and " { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; }\n") + set ok to append_list(lines and " return (long long)tg;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long add_task_group(long long group_ptr, long long fut_ptr) {\n") + set ok to append_list(lines and " EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n") + set ok to append_list(lines and " EpFuture* fut = (EpFuture*)fut_ptr;\n") + set ok to append_list(lines and " if (!tg || !fut) return 0;\n") + set ok to append_list(lines and " if (tg->count < 128) {\n") + set ok to append_list(lines and " tg->futures[tg->count++] = fut;\n") + set ok to append_list(lines and " // Associate the task's parent with the current task so it's cancellation-linked\n") + set ok to append_list(lines and " EpTask* task = ep_find_task_by_future(fut);\n") + set ok to append_list(lines and " if (task) {\n") + set ok to append_list(lines and " task->parent = ep_current_task;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long wait_task_group(long long group_ptr) {\n") + set ok to append_list(lines and " EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n") + set ok to append_list(lines and " if (!tg) return 0;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " int all_done = 0;\n") + set ok to append_list(lines and " while (!all_done) {\n") + set ok to append_list(lines and " all_done = 1;\n") + set ok to append_list(lines and " for (int i = 0; i < tg->count; i++) {\n") + set ok to append_list(lines and " EpFuture* fut = tg->futures[i];\n") + set ok to append_list(lines and " if (!fut->completed) {\n") + set ok to append_list(lines and " all_done = 0;\n") + set ok to append_list(lines and " break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " if (all_done) break;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " if (ep_run_queue_head) {\n") + set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\n") + set ok to append_list(lines and " if (task) {\n") + set ok to append_list(lines and " if (task->is_cancelled) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " task->fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " EpTask* saved_current = ep_current_task;\n") + set ok to append_list(lines and " ep_current_task = task;\n") + set ok to append_list(lines and " long long res = task->step(task->args);\n") + set ok to append_list(lines and " ep_current_task = saved_current;\n") + set ok to append_list(lines and " if (res != -999999) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->value = res;\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " if (task->fut->waiting_task) {\n") + set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n") + set ok to append_list(lines and " task->fut->waiting_task = NULL;\n") + return join_strings(lines) + +define ep_rt_core_4: + set lines to create_list() + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n") + set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n") + set ok to append_list(lines and " fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n") + set ok to append_list(lines and " exit(1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n") + set ok to append_list(lines and " if (timeout > 0) {\n") + set ok to append_list(lines and " ep_sleep_ms(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_process_expired_timers();\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " ep_async_wait_step(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " // Propagate cancellation/failure inside task group\n") + set ok to append_list(lines and " for (int i = 0; i < tg->count; i++) {\n") + set ok to append_list(lines and " EpFuture* fut = tg->futures[i];\n") + set ok to append_list(lines and " if (fut->completed && fut->value == -1) {\n") + set ok to append_list(lines and " tg->has_error = 1;\n") + set ok to append_list(lines and " for (int j = 0; j < tg->count; j++) {\n") + set ok to append_list(lines and " EpFuture* other_fut = tg->futures[j];\n") + set ok to append_list(lines and " if (!other_fut->completed) {\n") + set ok to append_list(lines and " EpTask* other_task = ep_find_task_by_future(other_fut);\n") + set ok to append_list(lines and " if (other_task) {\n") + set ok to append_list(lines and " ep_cancel_task(other_task);\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " other_fut->completed = 1;\n") + set ok to append_list(lines and " other_fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " for (int i = 0; i < tg->count; i++) {\n") + set ok to append_list(lines and " append_list(list, tg->futures[i]->value);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long ep_timeout_timer_step(void* r) {\n") + set ok to append_list(lines and " EpTimeoutArgs* args = (EpTimeoutArgs*)r;\n") + set ok to append_list(lines and " if (args && args->fut && !args->fut->completed) {\n") + set ok to append_list(lines and " args->timer_fired = 1;\n") + set ok to append_list(lines and " EpTask* task = ep_find_task_by_future(args->fut);\n") + set ok to append_list(lines and " if (task) {\n") + set ok to append_list(lines and " ep_cancel_task(task);\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " args->fut->completed = 1;\n") + set ok to append_list(lines and " args->fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long async_timeout(long long timeout_ms, long long fut_ptr) {\n") + set ok to append_list(lines and " EpFuture* fut = (EpFuture*)fut_ptr;\n") + set ok to append_list(lines and " if (!fut) return -1;\n") + set ok to append_list(lines and " if (fut->completed) return fut->value;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs));\n") + set ok to append_list(lines and " args->fut = fut;\n") + set ok to append_list(lines and " args->timer_fired = 0;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask));\n") + set ok to append_list(lines and " timer_task->step = ep_timeout_timer_step;\n") + set ok to append_list(lines and " timer_task->args = args;\n") + set ok to append_list(lines and " timer_task->args_size_bytes = sizeof(EpTimeoutArgs);\n") + set ok to append_list(lines and " timer_task->fut = NULL;\n") + set ok to append_list(lines and " timer_task->state = 0;\n") + set ok to append_list(lines and " timer_task->is_cancelled = 0;\n") + set ok to append_list(lines and " timer_task->parent = NULL;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " ep_async_register_timer(timeout_ms, timer_task);\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " while (!fut->completed && !(args->timer_fired)) {\n") + set ok to append_list(lines and " if (ep_run_queue_head) {\n") + set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\n") + set ok to append_list(lines and " if (task) {\n") + set ok to append_list(lines and " if (task->is_cancelled) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " task->fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " EpTask* saved_current = ep_current_task;\n") + set ok to append_list(lines and " ep_current_task = task;\n") + set ok to append_list(lines and " long long res = task->step(task->args);\n") + set ok to append_list(lines and " ep_current_task = saved_current;\n") + set ok to append_list(lines and " if (res != -999999) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " task->fut->value = res;\n") + set ok to append_list(lines and " task->fut->completed = 1;\n") + set ok to append_list(lines and " if (task->fut->waiting_task) {\n") + set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n") + set ok to append_list(lines and " task->fut->waiting_task = NULL;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(task->args);\n") + set ok to append_list(lines and " free(task);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n") + set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n") + set ok to append_list(lines and " break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n") + set ok to append_list(lines and " if (timeout > 0) {\n") + set ok to append_list(lines and " ep_sleep_ms(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_process_expired_timers();\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " ep_async_wait_step(timeout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " return fut->value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ── Awaitable async socket-readability ─────────────────────────────────────\n") + set ok to append_list(lines and " `await async_wait_readable(fd)` suspends the calling async task until `fd` is\n") + set ok to append_list(lines and " readable, letting the event loop run other tasks (e.g. another agent waiting on\n") + set ok to append_list(lines and " its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a\n") + set ok to append_list(lines and " oneshot read-readiness task with the loop, return the future. When fd becomes\n") + set ok to append_list(lines and " readable, ep_async_wait_step re-enqueues the task; its step completes the future\n") + set ok to append_list(lines and " and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n") + set ok to append_list(lines and " on ONE thread — no OS threads, no shared-heap GC race. */\n") + set ok to append_list(lines and "typedef struct { EpFuture* fut; } EpReadReadyArgs;\n") + set ok to append_list(lines and "static long long ep_read_ready_step(void* r) {\n") + set ok to append_list(lines and " EpReadReadyArgs* args = (EpReadReadyArgs*)r;\n") + set ok to append_list(lines and " if (args && args->fut) {\n") + set ok to append_list(lines and " args->fut->completed = 1;\n") + set ok to append_list(lines and " args->fut->value = 1;\n") + set ok to append_list(lines and " if (args->fut->waiting_task) {\n") + return join_strings(lines) + +define ep_rt_core_5: + set lines to create_list() + set ok to append_list(lines and " ep_task_enqueue(args->fut->waiting_task);\n") + set ok to append_list(lines and " args->fut->waiting_task = NULL;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long async_wait_readable(long long fd) {\n") + set ok to append_list(lines and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n") + set ok to append_list(lines and " fut->completed = 0;\n") + set ok to append_list(lines and " fut->value = 0;\n") + set ok to append_list(lines and " fut->waiting_task = NULL;\n") + set ok to append_list(lines and " fut->chan = 0;\n") + set ok to append_list(lines and " { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n") + set ok to append_list(lines and " EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs));\n") + set ok to append_list(lines and " args->fut = fut;\n") + set ok to append_list(lines and " EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n") + set ok to append_list(lines and " task->step = ep_read_ready_step;\n") + set ok to append_list(lines and " task->args = args;\n") + set ok to append_list(lines and " task->args_size_bytes = sizeof(EpReadReadyArgs);\n") + set ok to append_list(lines and " task->fut = NULL;\n") + set ok to append_list(lines and " task->state = 0;\n") + set ok to append_list(lines and " task->is_cancelled = 0;\n") + set ok to append_list(lines and " task->parent = ep_current_task;\n") + set ok to append_list(lines and " ep_async_register_read((int)fd, task);\n") + set ok to append_list(lines and " return (long long)fut;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " EpFuture* fut;\n") + set ok to append_list(lines and "} EpSleepTimerArgs;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long ep_sleep_timer_step(void* r) {\n") + set ok to append_list(lines and " EpSleepTimerArgs* args = (EpSleepTimerArgs*)r;\n") + set ok to append_list(lines and " if (args && args->fut) {\n") + set ok to append_list(lines and " args->fut->completed = 1;\n") + set ok to append_list(lines and " args->fut->value = 0;\n") + set ok to append_list(lines and " if (args->fut->waiting_task) {\n") + set ok to append_list(lines and " ep_task_enqueue(args->fut->waiting_task);\n") + set ok to append_list(lines and " args->fut->waiting_task = NULL;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long sleep_ms(long long ms) {\n") + set ok to append_list(lines and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n") + set ok to append_list(lines and " fut->completed = 0;\n") + set ok to append_list(lines and " fut->value = 0;\n") + set ok to append_list(lines and " fut->waiting_task = NULL;\n") + set ok to append_list(lines and " fut->chan = 0;\n") + set ok to append_list(lines and " { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs));\n") + set ok to append_list(lines and " args->fut = fut;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n") + set ok to append_list(lines and " task->step = ep_sleep_timer_step;\n") + set ok to append_list(lines and " task->args = args;\n") + set ok to append_list(lines and " task->args_size_bytes = sizeof(EpSleepTimerArgs);\n") + set ok to append_list(lines and " task->fut = NULL;\n") + set ok to append_list(lines and " task->state = 0;\n") + set ok to append_list(lines and " task->is_cancelled = 0;\n") + set ok to append_list(lines and " task->parent = ep_current_task;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " ep_async_register_timer(ms, task);\n") + set ok to append_list(lines and " return (long long)fut;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static long long cancel_task(long long fut_ptr) {\n") + set ok to append_list(lines and " EpFuture* fut = (EpFuture*)fut_ptr;\n") + set ok to append_list(lines and " if (fut) {\n") + set ok to append_list(lines and " EpTask* task = ep_find_task_by_future(fut);\n") + set ok to append_list(lines and " if (task) {\n") + set ok to append_list(lines and " ep_cancel_task(task);\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " fut->completed = 1;\n") + set ok to append_list(lines and " fut->value = -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Closure environment — captures travel with the function pointer */\n") + set ok to append_list(lines and "#define EP_CLOSURE_MAGIC 0x4550434C4FL\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " long long magic;\n") + set ok to append_list(lines and " long long fn_ptr;\n") + set ok to append_list(lines and " long long env[]; /* flexible array of captured values */\n") + set ok to append_list(lines and "} EpClosure;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* GC globals */\n") + set ok to append_list(lines and "static EpGCObject* ep_gc_head = NULL;\n") + set ok to append_list(lines and "static long long ep_gc_count = 0;\n") + set ok to append_list(lines and "static long long ep_gc_threshold = 4096;\n") + set ok to append_list(lines and "static int ep_gc_enabled = 1;\n") + set ok to append_list(lines and "static long long ep_gc_nursery_count = 0;\n") + set ok to append_list(lines and "static long long ep_gc_nursery_threshold = 512;\n") + set ok to append_list(lines and "static int ep_gc_minor_count = 0;\n") + set ok to append_list(lines and "static int ep_gc_major_count = 0;\n") + set ok to append_list(lines and "static void** ep_gc_remembered_set = NULL;\n") + set ok to append_list(lines and "static long long ep_gc_remembered_cap = 0;\n") + set ok to append_list(lines and "static long long ep_gc_remembered_size = 0;\n") + set ok to append_list(lines and "/* Single mutex for ALL GC and thread registry operations.\n") + set ok to append_list(lines and " Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex)\n") + set ok to append_list(lines and " which caused deadlock under concurrent channel load: thread A held gc_mutex\n") + set ok to append_list(lines and " and waited for registry_mutex, thread B held registry_mutex and waited for\n") + set ok to append_list(lines and " gc_mutex. Single lock eliminates the ordering problem. */\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and "#define __thread\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in\n") + set ok to append_list(lines and " ep_gc_stop_the_world(), waits until every *other* registered thread has parked\n") + set ok to append_list(lines and " at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs\n") + set ok to append_list(lines and " concurrently with a mutator changing its roots or an object's fields — the\n") + set ok to append_list(lines and " \"marking races with running mutators\" hazard. All three fields are touched\n") + set ok to append_list(lines and " only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at\n") + set ok to append_list(lines and " safepoints are a benign optimization: a missed set just defers parking to the\n") + set ok to append_list(lines and " next safepoint, and the collector's bounded wait covers it). */\n") + set ok to append_list(lines and "static volatile int ep_gc_stop_requested = 0;\n") + set ok to append_list(lines and "static int ep_gc_parked_count = 0;\n") + set ok to append_list(lines and "static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Function pointer for channel scanning — set after EpChannel is defined.\n") + set ok to append_list(lines and " GC mark calls this to scan values in-transit in channel buffers. */\n") + set ok to append_list(lines and "static void (*ep_gc_scan_channels_major)(void) = NULL;\n") + set ok to append_list(lines and "static void (*ep_gc_scan_channels_minor)(void) = NULL;\n") + set ok to append_list(lines and "/* Function pointers for marking top-level constant/global variables, which are\n") + set ok to append_list(lines and " GC roots that live outside any function frame. Set by __ep_init_constants. */\n") + set ok to append_list(lines and "static void (*ep_gc_mark_globals_major)(void) = NULL;\n") + set ok to append_list(lines and "static void (*ep_gc_mark_globals_minor)(void) = NULL;\n") + set ok to append_list(lines and "/* Function pointers for map value traversal — set after EpMap is defined.\n") + set ok to append_list(lines and " GC mark calls these to recursively mark values stored in maps. */\n") + set ok to append_list(lines and "static void (*ep_gc_mark_map_values)(void* ptr) = NULL;\n") + set ok to append_list(lines and "static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Thread registry for GC root scanning in multi-threaded environment */\n") + set ok to append_list(lines and "#define EP_MAX_THREADS 256\n") + set ok to append_list(lines and "static __thread void* volatile ep_thread_local_top = NULL;\n") + set ok to append_list(lines and "static __thread void* ep_thread_local_bottom = NULL;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n") + set ok to append_list(lines and "static void* ep_thread_bottoms[EP_MAX_THREADS];\n") + set ok to append_list(lines and "static volatile int ep_thread_active[EP_MAX_THREADS];\n") + set ok to append_list(lines and "static int ep_num_threads = 0;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Per-thread GC root state — heap-allocated, stable across thread lifetime.\n") + set ok to append_list(lines and " Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,\n") + set ok to append_list(lines and " ep_gc_root_sp) in the global registry. When a thread exited, the __thread\n") + return join_strings(lines) + +define ep_rt_core_6: + set lines to create_list() + set ok to append_list(lines and " storage was freed, leaving dangling pointers that ep_gc_mark would\n") + set ok to append_list(lines and " dereference → segfault. Now each thread gets a heap-allocated state struct\n") + set ok to append_list(lines and " that survives thread exit and is only recycled when the slot is reused. */\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " long long* roots[4096]; /* copy of root pointers, updated under lock */\n") + set ok to append_list(lines and " volatile int sp; /* current root stack pointer */\n") + set ok to append_list(lines and "} EpThreadGCState;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS];\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */\n") + set ok to append_list(lines and "#define EP_GC_MAX_ROOTS 4096\n") + set ok to append_list(lines and "static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS];\n") + set ok to append_list(lines and "static __thread int ep_gc_root_sp = 0;\n") + set ok to append_list(lines and "static __thread int ep_thread_slot = -1;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on\n") + set ok to append_list(lines and " push and retreats on pop so that per-frame push/pop counts stay balanced.\n") + set ok to append_list(lines and " Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further\n") + set ok to append_list(lines and " roots are counted but not stored (those deep-overflow locals are simply not\n") + set ok to append_list(lines and " traced) — crucially, we never overwrite or drop an outer frame's stored\n") + set ok to append_list(lines and " roots, which the old \"silently skip the push but still pop\" path did. */\n") + set ok to append_list(lines and "static void ep_gc_push_root(long long* root) {\n") + set ok to append_list(lines and " int idx = ep_gc_root_sp;\n") + set ok to append_list(lines and " ep_gc_root_sp++;\n") + set ok to append_list(lines and " if (idx < EP_GC_MAX_ROOTS) {\n") + set ok to append_list(lines and " ep_gc_root_stack[idx] = root;\n") + set ok to append_list(lines and " /* Update the heap-allocated state so GC mark can see it safely */\n") + set ok to append_list(lines and " if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n") + set ok to append_list(lines and " ep_thread_gc_states[ep_thread_slot]->roots[idx] = root;\n") + set ok to append_list(lines and " ep_thread_gc_states[ep_thread_slot]->sp =\n") + set ok to append_list(lines and " (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "static void ep_gc_pop_roots(long long count) {\n") + set ok to append_list(lines and " ep_gc_root_sp -= (int)count;\n") + set ok to append_list(lines and " if (ep_gc_root_sp < 0) ep_gc_root_sp = 0;\n") + set ok to append_list(lines and " /* Update the heap-allocated state (clamped to the array bound) */\n") + set ok to append_list(lines and " if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n") + set ok to append_list(lines and " ep_thread_gc_states[ep_thread_slot]->sp =\n") + set ok to append_list(lines and " (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Park the calling thread if the collector has stopped the world.\n") + set ok to append_list(lines and " MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise\n") + set ok to append_list(lines and " root set) is stable while parked, so the collector can scan it race-free. */\n") + set ok to append_list(lines and "static void ep_gc_park_if_stopped(void) {\n") + set ok to append_list(lines and " if (!ep_gc_stop_requested) return;\n") + set ok to append_list(lines and " /* Spill registers onto the stack and publish this thread's current stack top\n") + set ok to append_list(lines and " so the collector can conservatively scan its frozen C stack while parked —\n") + set ok to append_list(lines and " this catches roots held only in registers/temporaries that the precise\n") + set ok to append_list(lines and " shadow stack does not yet record. _dummy is declared below _pregs, so its\n") + set ok to append_list(lines and " (lower) address bounds a scan range that covers the spilled registers. */\n") + set ok to append_list(lines and " jmp_buf _pregs;\n") + set ok to append_list(lines and " volatile char _top_marker; /* function-scope: stays valid while parked */\n") + set ok to append_list(lines and " memset(&_pregs, 0, sizeof(_pregs));\n") + set ok to append_list(lines and " setjmp(_pregs);\n") + set ok to append_list(lines and " /* _top_marker is declared after _pregs, so its (lower) address bounds a scan\n") + set ok to append_list(lines and " range [&_top_marker, stack_bottom] that covers the spilled registers. */\n") + set ok to append_list(lines and " ep_thread_local_top = (void*)&_top_marker;\n") + set ok to append_list(lines and " __sync_synchronize(); /* publish shadow-stack + top writes before parking */\n") + set ok to append_list(lines and " ep_gc_parked_count++;\n") + set ok to append_list(lines and " while (ep_gc_stop_requested) {\n") + set ok to append_list(lines and " pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_parked_count--;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held.\n") + set ok to append_list(lines and " Waits (briefly releasing the lock so blocked mutators can reach a safepoint)\n") + set ok to append_list(lines and " until all other registered threads have parked. After a bounded fallback\n") + set ok to append_list(lines and " (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked\n") + set ok to append_list(lines and " or idle with a stable shadow stack, so scanning it is still safe in practice. */\n") + set ok to append_list(lines and "static void ep_gc_stop_the_world(void) {\n") + set ok to append_list(lines and " ep_gc_stop_requested = 1;\n") + set ok to append_list(lines and " /* Actively-running threads reach a safepoint (every allocation and every\n") + set ok to append_list(lines and " function entry) within microseconds, so they park on the first spin or\n") + set ok to append_list(lines and " two. The bound only caps the rare case where a thread is blocked/idle\n") + set ok to append_list(lines and " (e.g. just entered a channel op) and won't park — those have a stable\n") + set ok to append_list(lines and " shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */\n") + set ok to append_list(lines and " for (int spins = 0; spins < 40; spins++) {\n") + set ok to append_list(lines and " int others = 0;\n") + set ok to append_list(lines and " for (int t = 0; t < ep_num_threads; t++) {\n") + set ok to append_list(lines and " if (ep_thread_active[t] && t != ep_thread_slot) others++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (others <= 0 || ep_gc_parked_count >= others) return;\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " Sleep(1);\n") + set ok to append_list(lines and "#elif !defined(__wasm__)\n") + set ok to append_list(lines and " usleep(250);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */\n") + set ok to append_list(lines and "static void ep_gc_start_the_world(void) {\n") + set ok to append_list(lines and " ep_gc_stop_requested = 0;\n") + set ok to append_list(lines and " pthread_cond_broadcast(&ep_gc_resume_cond);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_register_thread(void* stack_bottom) {\n") + set ok to append_list(lines and " ep_thread_local_bottom = stack_bottom;\n") + set ok to append_list(lines and " ep_thread_local_top = stack_bottom;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " int slot = -1;\n") + set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n") + set ok to append_list(lines and " if (!ep_thread_active[i]) {\n") + set ok to append_list(lines and " slot = i;\n") + set ok to append_list(lines and " break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {\n") + set ok to append_list(lines and " slot = ep_num_threads++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (slot != -1) {\n") + set ok to append_list(lines and " ep_thread_tops[slot] = &ep_thread_local_top;\n") + set ok to append_list(lines and " ep_thread_bottoms[slot] = stack_bottom;\n") + set ok to append_list(lines and " /* Allocate or reuse heap state for this slot */\n") + set ok to append_list(lines and " if (!ep_thread_gc_states[slot]) {\n") + set ok to append_list(lines and " ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState));\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_thread_gc_states[slot]->sp = 0;\n") + set ok to append_list(lines and " ep_thread_slot = slot;\n") + set ok to append_list(lines and " __sync_synchronize(); /* Memory barrier: state must be visible before active */\n") + set ok to append_list(lines and " ep_thread_active[slot] = 1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_unregister_thread(void) {\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n") + set ok to append_list(lines and " if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n") + set ok to append_list(lines and " /* Zero root count FIRST — even if ep_gc_mark races past the\n") + set ok to append_list(lines and " active check, it will see sp=0 and walk no roots instead\n") + set ok to append_list(lines and " of dereferencing stale __thread pointers */\n") + set ok to append_list(lines and " if (ep_thread_gc_states[i]) {\n") + set ok to append_list(lines and " ep_thread_gc_states[i]->sp = 0;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */\n") + set ok to append_list(lines and " ep_thread_active[i] = 0;\n") + set ok to append_list(lines and " ep_thread_slot = -1;\n") + set ok to append_list(lines and " break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + return join_strings(lines) + +define ep_rt_core_7: + set lines to create_list() + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " void* key;\n") + set ok to append_list(lines and " EpGCObject* value;\n") + set ok to append_list(lines and "} EpGCEntry;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static EpGCEntry* ep_gc_table = NULL;\n") + set ok to append_list(lines and "static long long ep_gc_table_cap = 0;\n") + set ok to append_list(lines and "static long long ep_gc_table_size = 0;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap)\n") + set ok to append_list(lines and " with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4\n") + set ok to append_list(lines and " bits are always 0 and only every 16th bucket was ever a home slot. That caused\n") + set ok to append_list(lines and " catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's\n") + set ok to append_list(lines and " rehash became O(n^2), which (under the single global GC mutex) wedged the whole\n") + set ok to append_list(lines and " node when a large object list was freed. A splitmix64 finalizer avalanches all\n") + set ok to append_list(lines and " bits, so even the low bits taken by the (cap-1) mask are well distributed. */\n") + set ok to append_list(lines and "static inline long long ep_gc_index(void* key, long long cap) {\n") + set ok to append_list(lines and " uint64_t z = (uint64_t)(uintptr_t)key;\n") + set ok to append_list(lines and " z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;\n") + set ok to append_list(lines and " z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;\n") + set ok to append_list(lines and " z = z ^ (z >> 31);\n") + set ok to append_list(lines and " return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Insert without growing — assumes a free slot exists. Used by the resize and by\n") + set ok to append_list(lines and " ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of\n") + set ok to append_list(lines and " the table mid-iteration. */\n") + set ok to append_list(lines and "static void ep_gc_table_place(void* key, EpGCObject* value) {\n") + set ok to append_list(lines and " long long idx = ep_gc_index(key, ep_gc_table_cap);\n") + set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n") + set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n") + set ok to append_list(lines and " ep_gc_table[idx].value = value;\n") + set ok to append_list(lines and " return;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " idx = (idx + 1) & (ep_gc_table_cap - 1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_table[idx].key = key;\n") + set ok to append_list(lines and " ep_gc_table[idx].value = value;\n") + set ok to append_list(lines and " ep_gc_table_size++;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_table_insert(void* key, EpGCObject* value) {\n") + set ok to append_list(lines and " if (ep_gc_table_size * 2 >= ep_gc_table_cap) {\n") + set ok to append_list(lines and " long long old_cap = ep_gc_table_cap;\n") + set ok to append_list(lines and " long long new_cap = old_cap == 0 ? 512 : old_cap * 2;\n") + set ok to append_list(lines and " EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));\n") + set ok to append_list(lines and " EpGCEntry* old_table = ep_gc_table;\n") + set ok to append_list(lines and " ep_gc_table = new_table;\n") + set ok to append_list(lines and " ep_gc_table_cap = new_cap;\n") + set ok to append_list(lines and " ep_gc_table_size = 0;\n") + set ok to append_list(lines and " for (long long i = 0; i < old_cap; i++) {\n") + set ok to append_list(lines and " if (old_table[i].key != NULL) {\n") + set ok to append_list(lines and " ep_gc_table_place(old_table[i].key, old_table[i].value);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(old_table);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_table_place(key, value);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static EpGCObject* ep_gc_table_get(void* key) {\n") + set ok to append_list(lines and " if (ep_gc_table_cap == 0) return NULL;\n") + set ok to append_list(lines and " long long idx = ep_gc_index(key, ep_gc_table_cap);\n") + set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n") + set ok to append_list(lines and " if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;\n") + set ok to append_list(lines and " idx = (idx + 1) & (ep_gc_table_cap - 1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return NULL;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_table_remove(void* key) {\n") + set ok to append_list(lines and " if (ep_gc_table_cap == 0) return;\n") + set ok to append_list(lines and " long long idx = ep_gc_index(key, ep_gc_table_cap);\n") + set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n") + set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n") + set ok to append_list(lines and " ep_gc_table[idx].key = NULL;\n") + set ok to append_list(lines and " ep_gc_table[idx].value = NULL;\n") + set ok to append_list(lines and " ep_gc_table_size--;\n") + set ok to append_list(lines and " /* Backward-shift rehash of the rest of this cluster. Re-place (no\n") + set ok to append_list(lines and " resize: size is not growing) so a mid-iteration realloc can never\n") + set ok to append_list(lines and " free the table out from under this loop. */\n") + set ok to append_list(lines and " long long next_idx = (idx + 1) & (ep_gc_table_cap - 1);\n") + set ok to append_list(lines and " while (ep_gc_table[next_idx].key != NULL) {\n") + set ok to append_list(lines and " void* rehash_key = ep_gc_table[next_idx].key;\n") + set ok to append_list(lines and " EpGCObject* rehash_val = ep_gc_table[next_idx].value;\n") + set ok to append_list(lines and " ep_gc_table[next_idx].key = NULL;\n") + set ok to append_list(lines and " ep_gc_table[next_idx].value = NULL;\n") + set ok to append_list(lines and " ep_gc_table_size--;\n") + set ok to append_list(lines and " ep_gc_table_place(rehash_key, rehash_val);\n") + set ok to append_list(lines and " next_idx = (next_idx + 1) & (ep_gc_table_cap - 1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " idx = (idx + 1) & (ep_gc_table_cap - 1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Register a new GC object */\n") + set ok to append_list(lines and "static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {\n") + set ok to append_list(lines and " if (!ptr) return NULL;\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */\n") + set ok to append_list(lines and " EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));\n") + set ok to append_list(lines and " if (!obj) {\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and " return NULL;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " obj->kind = kind;\n") + set ok to append_list(lines and " obj->marked = 0;\n") + set ok to append_list(lines and " obj->ptr = ptr;\n") + set ok to append_list(lines and " obj->size = 0;\n") + set ok to append_list(lines and " obj->num_fields = 0;\n") + set ok to append_list(lines and " obj->generation = 0;\n") + set ok to append_list(lines and " obj->next = ep_gc_head;\n") + set ok to append_list(lines and " ep_gc_head = obj;\n") + set ok to append_list(lines and " ep_gc_count++;\n") + set ok to append_list(lines and " ep_gc_nursery_count++;\n") + set ok to append_list(lines and " ep_gc_table_insert(ptr, obj);\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and " return obj;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Find GC object by pointer.\n") + set ok to append_list(lines and " Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table\n") + set ok to append_list(lines and " concurrently (from another thread's allocation). Mutator-side callers\n") + set ok to append_list(lines and " (write barrier, free_struct/free_map/free_list, to-string) must use this\n") + set ok to append_list(lines and " locking variant; code already holding the mutex (mark/sweep) calls\n") + set ok to append_list(lines and " ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */\n") + set ok to append_list(lines and "static EpGCObject* ep_gc_find(void* ptr) {\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint */\n") + set ok to append_list(lines and " EpGCObject* obj = ep_gc_table_get(ptr);\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and " return obj;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).\n") + set ok to append_list(lines and " The whole operation runs under ep_gc_mutex so the table lookups and the\n") + set ok to append_list(lines and " remembered-set update see a consistent table (no race with a concurrent\n") + set ok to append_list(lines and " resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */\n") + set ok to append_list(lines and "static void ep_gc_write_barrier(void* host_ptr, long long val) {\n") + set ok to append_list(lines and " if (val == 0) return;\n") + return join_strings(lines) + +define ep_rt_core_8: + set lines to create_list() + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */\n") + set ok to append_list(lines and " EpGCObject* host_obj = ep_gc_table_get(host_ptr);\n") + set ok to append_list(lines and " EpGCObject* val_obj = ep_gc_table_get((void*)val);\n") + set ok to append_list(lines and " if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) {\n") + set ok to append_list(lines and " /* Check if already in remembered set */\n") + set ok to append_list(lines and " int found = 0;\n") + set ok to append_list(lines and " for (long long i = 0; i < ep_gc_remembered_size; i++) {\n") + set ok to append_list(lines and " if (ep_gc_remembered_set[i] == (void*)val) {\n") + set ok to append_list(lines and " found = 1;\n") + set ok to append_list(lines and " break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (!found) {\n") + set ok to append_list(lines and " if (ep_gc_remembered_size >= ep_gc_remembered_cap) {\n") + set ok to append_list(lines and " long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2;\n") + set ok to append_list(lines and " void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*));\n") + set ok to append_list(lines and " if (new_set) {\n") + set ok to append_list(lines and " ep_gc_remembered_set = new_set;\n") + set ok to append_list(lines and " ep_gc_remembered_cap = new_cap;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (ep_gc_remembered_size < ep_gc_remembered_cap) {\n") + set ok to append_list(lines and " ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Forward declarations for list type (needed by GC mark) */\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " long long* data;\n") + set ok to append_list(lines and " long long length;\n") + set ok to append_list(lines and " long long capacity;\n") + set ok to append_list(lines and "} EpList;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* A real heap object (list/map/string) is malloc'd, so its address is far above\n") + set ok to append_list(lines and " the never-mapped first page. EP values that are NOT pointers — small ints,\n") + set ok to append_list(lines and " booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096).\n") + set ok to append_list(lines and " Guarding the object accessors with this turns \"deref a non-pointer\" (the cause\n") + set ok to append_list(lines and " of the read_transcripts segfault, and that whole class) into a safe null return\n") + set ok to append_list(lines and " instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */\n") + set ok to append_list(lines and "#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL)\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Mark a single object and recursively mark its children */\n") + set ok to append_list(lines and "static void ep_gc_mark_object(void* ptr) {\n") + set ok to append_list(lines and " if (!ptr) return;\n") + set ok to append_list(lines and " /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n") + set ok to append_list(lines and " EpGCObject* obj = ep_gc_table_get(ptr);\n") + set ok to append_list(lines and " if (!obj || obj->marked) return;\n") + set ok to append_list(lines and " obj->marked = 1;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " if (obj->kind == EP_OBJ_LIST) {\n") + set ok to append_list(lines and " EpList* list = (EpList*)ptr;\n") + set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") + set ok to append_list(lines and " long long val = list->data[i];\n") + set ok to append_list(lines and " if (val != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_STRUCT) {\n") + set ok to append_list(lines and " long long* fields = (long long*)ptr;\n") + set ok to append_list(lines and " for (long long i = 0; i < obj->num_fields; i++) {\n") + set ok to append_list(lines and " if (fields[i] != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)fields[i]);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_MAP) {\n") + set ok to append_list(lines and " if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Mark a single object and recursively mark its children (only if it is Gen 0) */\n") + set ok to append_list(lines and "static void ep_gc_mark_object_minor(void* ptr) {\n") + set ok to append_list(lines and " if (!ptr) return;\n") + set ok to append_list(lines and " /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n") + set ok to append_list(lines and " EpGCObject* obj = ep_gc_table_get(ptr);\n") + set ok to append_list(lines and " if (!obj || obj->generation != 0 || obj->marked) return;\n") + set ok to append_list(lines and " obj->marked = 1;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " if (obj->kind == EP_OBJ_LIST) {\n") + set ok to append_list(lines and " EpList* list = (EpList*)ptr;\n") + set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") + set ok to append_list(lines and " long long val = list->data[i];\n") + set ok to append_list(lines and " if (val != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_STRUCT) {\n") + set ok to append_list(lines and " long long* fields = (long long*)ptr;\n") + set ok to append_list(lines and " for (long long i = 0; i < obj->num_fields; i++) {\n") + set ok to append_list(lines and " if (fields[i] != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)fields[i]);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_MAP) {\n") + set ok to append_list(lines and " if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Conservatively scan every registered thread's C stack and mark any word that\n") + set ok to append_list(lines and " looks like a tracked pointer. The collector spills its own registers and\n") + set ok to append_list(lines and " publishes its top here; all other threads are parked at a safepoint with their\n") + set ok to append_list(lines and " registers spilled and top published (ep_gc_park_if_stopped), so their stacks\n") + set ok to append_list(lines and " are frozen. This complements the precise shadow stacks: it catches roots held\n") + set ok to append_list(lines and " only in registers/temporaries (e.g. a freshly allocated object not yet stored\n") + set ok to append_list(lines and " into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find.\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " Only run on MAJOR collections: minor collections rely on the precise shadow\n") + set ok to append_list(lines and " stacks plus the write barrier's remembered set (the standard generational\n") + set ok to append_list(lines and " approach), so they do no stack scan at all — which means there is no racy\n") + set ok to append_list(lines and " cross-thread stack read on the frequent minor path either. The expensive\n") + set ok to append_list(lines and " full-stack scan is paid only on the rarer major collection, where it pins\n") + set ok to append_list(lines and " any long-lived object reachable only via a register across many GCs.\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " Marked no_sanitize_address: a conservative scan deliberately reads whole stack\n") + set ok to append_list(lines and " ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */\n") + set ok to append_list(lines and "#if defined(__SANITIZE_ADDRESS__)\n") + set ok to append_list(lines and "# define EP_NO_ASAN __attribute__((no_sanitize_address))\n") + set ok to append_list(lines and "#elif defined(__has_feature)\n") + set ok to append_list(lines and "# if __has_feature(address_sanitizer)\n") + set ok to append_list(lines and "# define EP_NO_ASAN __attribute__((no_sanitize_address))\n") + set ok to append_list(lines and "# endif\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "#ifndef EP_NO_ASAN\n") + set ok to append_list(lines and "# define EP_NO_ASAN\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "EP_NO_ASAN\n") + set ok to append_list(lines and "static void ep_gc_scan_thread_stacks(void) {\n") + set ok to append_list(lines and " jmp_buf _regs;\n") + set ok to append_list(lines and " volatile char _top_marker;\n") + set ok to append_list(lines and " memset(&_regs, 0, sizeof(_regs));\n") + set ok to append_list(lines and " setjmp(_regs); /* spill the collector's own registers onto its stack */\n") + set ok to append_list(lines and " /* Publish the LOWEST of our own local addresses as this thread's live top, so the\n") + set ok to append_list(lines and " scanned range covers both the stack marker and the register-spill buffer whatever\n") + set ok to append_list(lines and " order the compiler laid them out (a missed _regs would drop a register-only root). */\n") + set ok to append_list(lines and " { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;\n") + set ok to append_list(lines and " ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }\n") + set ok to append_list(lines and " for (int t = 0; t < ep_num_threads; t++) {\n") + set ok to append_list(lines and " if (!ep_thread_active[t]) continue;\n") + set ok to append_list(lines and " if (!ep_thread_tops[t]) continue;\n") + set ok to append_list(lines and " /* The published top comes from a char local, so it may not be pointer-aligned;\n") + set ok to append_list(lines and " mask DOWN to 8 bytes. Aligning down only widens the conservative window by a\n") + set ok to append_list(lines and " few harmless bytes — aligning up could skip the slot holding a live root.\n") + set ok to append_list(lines and " Unaligned void** dereferences are UB and produce a skewed scan window on\n") + set ok to append_list(lines and " strict platforms (caught by valgrind on Linux). */\n") + set ok to append_list(lines and " void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);\n") + set ok to append_list(lines and " void** end = (void**)ep_thread_bottoms[t];\n") + set ok to append_list(lines and " if (!start || !end) continue;\n") + return join_strings(lines) + +define ep_rt_core_9: + set lines to create_list() + set ok to append_list(lines and " if (start > end) { void** tmp = start; start = end; end = tmp; }\n") + set ok to append_list(lines and " for (void** cur = start; cur < end; cur++) {\n") + set ok to append_list(lines and " void* p = *cur;\n") + set ok to append_list(lines and " if (p) ep_gc_mark_object(p);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Mark phase: traverse from ALL threads' explicit GC roots.\n") + set ok to append_list(lines and " Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */\n") + set ok to append_list(lines and "static void ep_gc_mark(void) {\n") + set ok to append_list(lines and " ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */\n") + set ok to append_list(lines and " for (int t = 0; t < ep_num_threads; t++) {\n") + set ok to append_list(lines and " if (!ep_thread_active[t]) continue;\n") + set ok to append_list(lines and " EpThreadGCState* state = ep_thread_gc_states[t];\n") + set ok to append_list(lines and " if (!state) continue;\n") + set ok to append_list(lines and " int sp = state->sp;\n") + set ok to append_list(lines and " if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n") + set ok to append_list(lines and " for (int i = 0; i < sp; i++) {\n") + set ok to append_list(lines and " long long* root_ptr = state->roots[i];\n") + set ok to append_list(lines and " if (!root_ptr) continue;\n") + set ok to append_list(lines and " long long val = *root_ptr;\n") + set ok to append_list(lines and " if (val != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Also mark from main thread's local root stack (thread 0 / unregistered) */\n") + set ok to append_list(lines and " int local_sp = ep_gc_root_sp;\n") + set ok to append_list(lines and " if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n") + set ok to append_list(lines and " for (int i = 0; i < local_sp; i++) {\n") + set ok to append_list(lines and " long long val = *ep_gc_root_stack[i];\n") + set ok to append_list(lines and " if (val != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Mark active tasks in the scheduler run queue */\n") + set ok to append_list(lines and " EpTask* task = ep_run_queue_head;\n") + set ok to append_list(lines and " while (task) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)task->fut);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (task->args && task->args_size_bytes > 0) {\n") + set ok to append_list(lines and " long long* ptr = (long long*)task->args;\n") + set ok to append_list(lines and " for (int i = 0; i < task->args_size_bytes / 8; i++) {\n") + set ok to append_list(lines and " long long val = ptr[i];\n") + set ok to append_list(lines and " if (val != 0) ep_gc_mark_object((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " task = task->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Mark active tasks in the timers queue */\n") + set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n") + set ok to append_list(lines and " while (timer) {\n") + set ok to append_list(lines and " if (timer->task) {\n") + set ok to append_list(lines and " EpTask* t = timer->task;\n") + set ok to append_list(lines and " if (t->fut) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)t->fut);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (t->args && t->args_size_bytes > 0) {\n") + set ok to append_list(lines and " long long* ptr = (long long*)t->args;\n") + set ok to append_list(lines and " for (int i = 0; i < t->args_size_bytes / 8; i++) {\n") + set ok to append_list(lines and " long long val = ptr[i];\n") + set ok to append_list(lines and " if (val != 0) ep_gc_mark_object((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " timer = timer->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Mark top-level constant/global variables (roots outside any frame) */\n") + set ok to append_list(lines and " if (ep_gc_mark_globals_major) ep_gc_mark_globals_major();\n") + set ok to append_list(lines and " /* Scan all registered channel buffers — values in-transit have no root */\n") + set ok to append_list(lines and " if (ep_gc_scan_channels_major) ep_gc_scan_channels_major();\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it\n") + set ok to append_list(lines and " finds. This closes a use-after-free on the frequent minor path: a freshly-allocated\n") + set ok to append_list(lines and " argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating\n") + set ok to append_list(lines and " h() — lives only on the C stack / in registers and is not yet on the precise shadow\n") + set ok to append_list(lines and " stack, so a minor collection triggered mid-expression would otherwise free it. Scanning\n") + set ok to append_list(lines and " ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap\n") + set ok to append_list(lines and " (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by\n") + set ok to append_list(lines and " ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held\n") + set ok to append_list(lines and " roots onto the stack so the scan can see them. */\n") + set ok to append_list(lines and "EP_NO_ASAN\n") + set ok to append_list(lines and "static void ep_gc_scan_own_stack_minor(void) {\n") + set ok to append_list(lines and " jmp_buf _regs;\n") + set ok to append_list(lines and " volatile char _marker;\n") + set ok to append_list(lines and " memset(&_regs, 0, sizeof(_regs));\n") + set ok to append_list(lines and " setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */\n") + set ok to append_list(lines and " void* bottom = ep_thread_local_bottom;\n") + set ok to append_list(lines and " if (!bottom) return;\n") + set ok to append_list(lines and " /* Start at the LOWEST of our own local addresses so the scanned range covers both\n") + set ok to append_list(lines and " the current stack top (_marker) and the register-spill buffer (_regs), regardless\n") + set ok to append_list(lines and " of how the compiler ordered these locals on the stack. Missing _regs would drop a\n") + set ok to append_list(lines and " root held only in a callee-saved register -> a rare use-after-free. */\n") + set ok to append_list(lines and " char* a = (char*)(void*)&_marker;\n") + set ok to append_list(lines and " char* b = (char*)(void*)&_regs;\n") + set ok to append_list(lines and " char* lo = (a < b) ? a : b;\n") + set ok to append_list(lines and " /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8\n") + set ok to append_list(lines and " bytes. Aligning down only widens the conservative window by a few harmless\n") + set ok to append_list(lines and " bytes — aligning up could skip the slot holding a live root. Unaligned void**\n") + set ok to append_list(lines and " dereferences are UB and skew the scan window on strict platforms (valgrind). */\n") + set ok to append_list(lines and " void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7);\n") + set ok to append_list(lines and " void** end = (void**)bottom;\n") + set ok to append_list(lines and " if (start > end) { void** tmp = start; start = end; end = tmp; }\n") + set ok to append_list(lines and " for (void** cur = start; cur < end; cur++) {\n") + set ok to append_list(lines and " void* p = *cur;\n") + set ok to append_list(lines and " if (p) ep_gc_mark_object_minor(p);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_mark_minor(void) {\n") + set ok to append_list(lines and " /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument\n") + set ok to append_list(lines and " temporaries (only on the stack / in registers, not yet on the shadow stack) that a\n") + set ok to append_list(lines and " minor collection mid-expression would otherwise free. Own-thread only, so race-free. */\n") + set ok to append_list(lines and " ep_gc_scan_own_stack_minor();\n") + set ok to append_list(lines and " for (int t = 0; t < ep_num_threads; t++) {\n") + set ok to append_list(lines and " if (!ep_thread_active[t]) continue;\n") + set ok to append_list(lines and " EpThreadGCState* state = ep_thread_gc_states[t];\n") + set ok to append_list(lines and " if (!state) continue;\n") + set ok to append_list(lines and " int sp = state->sp;\n") + set ok to append_list(lines and " if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n") + set ok to append_list(lines and " for (int i = 0; i < sp; i++) {\n") + set ok to append_list(lines and " long long* root_ptr = state->roots[i];\n") + set ok to append_list(lines and " if (!root_ptr) continue;\n") + set ok to append_list(lines and " long long val = *root_ptr;\n") + set ok to append_list(lines and " if (val != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " int local_sp = ep_gc_root_sp;\n") + set ok to append_list(lines and " if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n") + set ok to append_list(lines and " for (int i = 0; i < local_sp; i++) {\n") + set ok to append_list(lines and " long long val = *ep_gc_root_stack[i];\n") + set ok to append_list(lines and " if (val != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Mark active tasks in the scheduler run queue for minor collection */\n") + set ok to append_list(lines and " EpTask* task = ep_run_queue_head;\n") + set ok to append_list(lines and " while (task) {\n") + set ok to append_list(lines and " if (task->fut) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)task->fut);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (task->args && task->args_size_bytes > 0) {\n") + set ok to append_list(lines and " long long* ptr = (long long*)task->args;\n") + set ok to append_list(lines and " for (int i = 0; i < task->args_size_bytes / 8; i++) {\n") + set ok to append_list(lines and " long long val = ptr[i];\n") + return join_strings(lines) + +define ep_rt_core_10: + set lines to create_list() + set ok to append_list(lines and " if (val != 0) ep_gc_mark_object_minor((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " task = task->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Mark active tasks in the timers queue for minor collection */\n") + set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n") + set ok to append_list(lines and " while (timer) {\n") + set ok to append_list(lines and " if (timer->task) {\n") + set ok to append_list(lines and " EpTask* t = timer->task;\n") + set ok to append_list(lines and " if (t->fut) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)t->fut);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (t->args && t->args_size_bytes > 0) {\n") + set ok to append_list(lines and " long long* ptr = (long long*)t->args;\n") + set ok to append_list(lines and " for (int i = 0; i < t->args_size_bytes / 8; i++) {\n") + set ok to append_list(lines and " long long val = ptr[i];\n") + set ok to append_list(lines and " if (val != 0) ep_gc_mark_object_minor((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " timer = timer->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Also mark from the remembered set */\n") + set ok to append_list(lines and " for (long long i = 0; i < ep_gc_remembered_size; i++) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor(ep_gc_remembered_set[i]);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Mark top-level constant/global variables (roots outside any frame) */\n") + set ok to append_list(lines and " if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor();\n") + set ok to append_list(lines and " /* Scan all registered channel buffers — values in-transit have no root */\n") + set ok to append_list(lines and " if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor();\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_sweep_minor(void) {\n") + set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n") + set ok to append_list(lines and " while (*cur) {\n") + set ok to append_list(lines and " if ((*cur)->generation == 0) {\n") + set ok to append_list(lines and " if (!(*cur)->marked) {\n") + set ok to append_list(lines and " EpGCObject* garbage = *cur;\n") + set ok to append_list(lines and " *cur = garbage->next;\n") + set ok to append_list(lines and " ep_gc_table_remove(garbage->ptr);\n") + set ok to append_list(lines and " if (garbage->kind == EP_OBJ_LIST) {\n") + set ok to append_list(lines and " EpList* list = (EpList*)garbage->ptr;\n") + set ok to append_list(lines and " if (list) {\n") + set ok to append_list(lines and " free(list->data);\n") + set ok to append_list(lines and " free(list);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRING) {\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRUCT) {\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_CLOSURE) {\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_MAP) {\n") + set ok to append_list(lines and " /* EpMap layout: entries*, capacity, size. Free entries then map. */\n") + set ok to append_list(lines and " void** map_fields = (void**)garbage->ptr;\n") + set ok to append_list(lines and " if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(garbage);\n") + set ok to append_list(lines and " ep_gc_count--;\n") + set ok to append_list(lines and " ep_gc_nursery_count--;\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " (*cur)->marked = 0;\n") + set ok to append_list(lines and " (*cur)->generation = 1;\n") + set ok to append_list(lines and " ep_gc_nursery_count--;\n") + set ok to append_list(lines and " cur = &(*cur)->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " cur = &(*cur)->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_remembered_size = 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_sweep_major(void) {\n") + set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n") + set ok to append_list(lines and " while (*cur) {\n") + set ok to append_list(lines and " if (!(*cur)->marked) {\n") + set ok to append_list(lines and " EpGCObject* garbage = *cur;\n") + set ok to append_list(lines and " *cur = garbage->next;\n") + set ok to append_list(lines and " ep_gc_table_remove(garbage->ptr);\n") + set ok to append_list(lines and " if (garbage->generation == 0) {\n") + set ok to append_list(lines and " ep_gc_nursery_count--;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (garbage->kind == EP_OBJ_LIST) {\n") + set ok to append_list(lines and " EpList* list = (EpList*)garbage->ptr;\n") + set ok to append_list(lines and " if (list) {\n") + set ok to append_list(lines and " free(list->data);\n") + set ok to append_list(lines and " free(list);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRING) {\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRUCT) {\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_CLOSURE) {\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_MAP) {\n") + set ok to append_list(lines and " void** map_fields = (void**)garbage->ptr;\n") + set ok to append_list(lines and " if (map_fields && map_fields[0]) free(map_fields[0]);\n") + set ok to append_list(lines and " free(garbage->ptr);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(garbage);\n") + set ok to append_list(lines and " ep_gc_count--;\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " (*cur)->marked = 0;\n") + set ok to append_list(lines and " if ((*cur)->generation == 0) {\n") + set ok to append_list(lines and " (*cur)->generation = 1;\n") + set ok to append_list(lines and " ep_gc_nursery_count--;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " cur = &(*cur)->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_remembered_size = 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_collect_minor(void) {\n") + set ok to append_list(lines and " if (!ep_gc_enabled) return;\n") + set ok to append_list(lines and " ep_gc_minor_count++;\n") + set ok to append_list(lines and " ep_gc_mark_minor();\n") + set ok to append_list(lines and " ep_gc_sweep_minor();\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_collect_major(void) {\n") + set ok to append_list(lines and " if (!ep_gc_enabled) return;\n") + set ok to append_list(lines and " ep_gc_major_count++;\n") + set ok to append_list(lines and " ep_gc_mark();\n") + set ok to append_list(lines and " ep_gc_sweep_major();\n") + set ok to append_list(lines and " ep_gc_threshold = ep_gc_count * 2;\n") + set ok to append_list(lines and " if (ep_gc_threshold < 4096) ep_gc_threshold = 4096;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Run a full GC collection — caller MUST hold ep_gc_mutex */\n") + set ok to append_list(lines and "static void ep_gc_collect(void) {\n") + set ok to append_list(lines and " ep_gc_collect_major();\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function\n") + set ok to append_list(lines and " GC safepoint: if another thread has stopped the world, park here until it's done. */\n") + set ok to append_list(lines and "static void ep_gc_maybe_collect(void) {\n") + set ok to append_list(lines and " if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n") + set ok to append_list(lines and " /* Safepoint: lock-free fast check, then park under the lock if a collection\n") + set ok to append_list(lines and " is in progress on another thread. Keeps the no-GC path lock-free. */\n") + set ok to append_list(lines and " if (ep_gc_stop_requested) {\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " ep_gc_park_if_stopped();\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Fast path: check thresholds before acquiring mutex.\n") + set ok to append_list(lines and " Counters are only incremented under the mutex, so worst case\n") + return join_strings(lines) + +define ep_rt_core_11: + set lines to create_list() + set ok to append_list(lines and " we miss one collection cycle — safe trade-off for avoiding\n") + set ok to append_list(lines and " a mutex lock/unlock (~20-50ns) on every function call. */\n") + set ok to append_list(lines and " if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;\n") + set ok to append_list(lines and " EP_GC_UPDATE_TOP();\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " /* Another thread may have started collecting between the check and the lock —\n") + set ok to append_list(lines and " park instead of racing it, then re-check thresholds under the lock. */\n") + set ok to append_list(lines and " ep_gc_park_if_stopped();\n") + set ok to append_list(lines and " if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) {\n") + set ok to append_list(lines and " ep_gc_stop_the_world();\n") + set ok to append_list(lines and " if (ep_gc_nursery_count >= ep_gc_nursery_threshold) {\n") + set ok to append_list(lines and " ep_gc_collect_minor();\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (ep_gc_count >= ep_gc_threshold) {\n") + set ok to append_list(lines and " ep_gc_collect_major();\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_start_the_world();\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Unregister an object (for explicit free — removes from GC tracking) */\n") + set ok to append_list(lines and "static void ep_gc_unregister(void* ptr) {\n") + set ok to append_list(lines and " if (!ptr) return;\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") + set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */\n") + set ok to append_list(lines and " /* Clean up references from the remembered set to prevent dangling pointers */\n") + set ok to append_list(lines and " for (long long i = 0; i < ep_gc_remembered_size; ) {\n") + set ok to append_list(lines and " if (ep_gc_remembered_set[i] == ptr) {\n") + set ok to append_list(lines and " for (long long j = i; j < ep_gc_remembered_size - 1; j++) {\n") + set ok to append_list(lines and " ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1];\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_remembered_size--;\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " i++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_table_remove(ptr);\n") + set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n") + set ok to append_list(lines and " while (*cur) {\n") + set ok to append_list(lines and " if ((*cur)->ptr == ptr) {\n") + set ok to append_list(lines and " EpGCObject* found = *cur;\n") + set ok to append_list(lines and " *cur = found->next;\n") + set ok to append_list(lines and " if (found->generation == 0) {\n") + set ok to append_list(lines and " ep_gc_nursery_count--;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(found);\n") + set ok to append_list(lines and " ep_gc_count--;\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and " return;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " cur = &(*cur)->next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Cleanup all remaining GC objects (called at program exit) */\n") + set ok to append_list(lines and "static void ep_gc_shutdown(void) {\n") + set ok to append_list(lines and " ep_gc_enabled = 0;\n") + set ok to append_list(lines and " /* Only free GC bookkeeping structures, not the tracked objects themselves.\n") + set ok to append_list(lines and " The RAII auto-cleanup has already freed owned objects, and the OS will\n") + set ok to append_list(lines and " reclaim everything else on process exit. Attempting to free individual\n") + set ok to append_list(lines and " objects here causes double-free aborts when RAII and GC both track\n") + set ok to append_list(lines and " the same allocation. */\n") + set ok to append_list(lines and " EpGCObject* cur = ep_gc_head;\n") + set ok to append_list(lines and " while (cur) {\n") + set ok to append_list(lines and " EpGCObject* next = cur->next;\n") + set ok to append_list(lines and " free(cur); /* free the GCObject wrapper only */\n") + set ok to append_list(lines and " cur = next;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_head = NULL;\n") + set ok to append_list(lines and " ep_gc_count = 0;\n") + set ok to append_list(lines and " if (ep_gc_table) {\n") + set ok to append_list(lines and " free(ep_gc_table);\n") + set ok to append_list(lines and " ep_gc_table = NULL;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_table_cap = 0;\n") + set ok to append_list(lines and " ep_gc_table_size = 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== End Garbage Collector ========== */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long create_list(void);\n") + set ok to append_list(lines and "long long append_list(long long list_ptr, long long value);\n") + set ok to append_list(lines and "long long get_list(long long list_ptr, long long index);\n") + set ok to append_list(lines and "long long set_list(long long list_ptr, long long index, long long value);\n") + set ok to append_list(lines and "long long length_list(long long list_ptr);\n") + set ok to append_list(lines and "long long free_list(long long list_ptr);\n") + set ok to append_list(lines and "long long pop_list(long long list_ptr);\n") + set ok to append_list(lines and "long long remove_list(long long list_ptr, long long index);\n") + set ok to append_list(lines and "char* string_from_list(long long list_ptr);\n") + set ok to append_list(lines and "long long string_to_list(const char* s);\n") + set ok to append_list(lines and "long long string_length(const char* s);\n") + set ok to append_list(lines and "long long display_string(const char* s);\n") + set ok to append_list(lines and "long long file_read(long long path_val);\n") + set ok to append_list(lines and "long long file_write(long long path_val, long long content_val);\n") + set ok to append_list(lines and "long long file_append(long long path_val, long long content_val);\n") + set ok to append_list(lines and "long long file_exists(long long path_val);\n") + set ok to append_list(lines and "long long string_contains(long long s_val, long long sub_val);\n") + set ok to append_list(lines and "long long string_index_of(long long s_val, long long sub_val);\n") + set ok to append_list(lines and "long long string_replace(long long s_val, long long old_val, long long new_val);\n") + set ok to append_list(lines and "long long string_upper(long long s_val);\n") + set ok to append_list(lines and "long long string_lower(long long s_val);\n") + set ok to append_list(lines and "long long string_trim(long long s_val);\n") + set ok to append_list(lines and "long long string_split(long long s_val, long long delim_val);\n") + set ok to append_list(lines and "long long char_at(long long s_val, long long index);\n") + set ok to append_list(lines and "long long char_from_code(long long code);\n") + set ok to append_list(lines and "long long ep_abs(long long n);\n") + set ok to append_list(lines and "long long json_get_string(long long json_val, long long key_val);\n") + set ok to append_list(lines and "long long json_get_int(long long json_val, long long key_val);\n") + set ok to append_list(lines and "long long json_get_bool(long long json_val, long long key_val);\n") + set ok to append_list(lines and "long long ep_sha1(long long data_val);\n") + set ok to append_list(lines and "long long ep_net_recv_bytes(long long fd, long long count);\n") + set ok to append_list(lines and "long long channel_try_recv(long long chan_ptr, long long out_ptr);\n") + set ok to append_list(lines and "long long channel_has_data(long long chan_ptr);\n") + set ok to append_list(lines and "long long channel_select(long long channels_list, long long timeout_ms);\n") + set ok to append_list(lines and "long long ep_auto_to_string(long long val);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct EpChannel_ {\n") + set ok to append_list(lines and " long long* data;\n") + set ok to append_list(lines and " long long capacity;\n") + set ok to append_list(lines and " long long head;\n") + set ok to append_list(lines and " long long tail;\n") + set ok to append_list(lines and " long long size;\n") + set ok to append_list(lines and " ep_mutex_t mutex;\n") + set ok to append_list(lines and " ep_cond_t cond_recv;\n") + set ok to append_list(lines and " ep_cond_t cond_send;\n") + set ok to append_list(lines and "} EpChannel;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Global channel registry — allows GC to scan values in-transit in channel buffers.\n") + set ok to append_list(lines and " Without this, an object sent to a channel but not yet received has NO GC root:\n") + set ok to append_list(lines and " the sender has popped it, the receiver hasn't pushed it, and the channel buffer\n") + set ok to append_list(lines and " is not scanned. The GC sweeps it → receiver gets a dangling pointer. */\n") + set ok to append_list(lines and "#define EP_MAX_CHANNELS 1024\n") + set ok to append_list(lines and "static EpChannel* ep_channel_registry[EP_MAX_CHANNELS];\n") + set ok to append_list(lines and "static int ep_channel_count = 0;\n") + set ok to append_list(lines and "static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_register_channel(EpChannel* chan) {\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n") + set ok to append_list(lines and " if (ep_channel_count < EP_MAX_CHANNELS) {\n") + set ok to append_list(lines and " ep_channel_registry[ep_channel_count++] = chan;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_channel_registry_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Channel scanning implementations — called by GC mark via function pointers.\n") + set ok to append_list(lines and " These are defined here (after EpChannel) so they can access struct fields. */\n") + set ok to append_list(lines and "static void ep_gc_mark_object(void* ptr); /* forward decl */\n") + set ok to append_list(lines and "static void ep_gc_mark_object_minor(void* ptr); /* forward decl */\n") + return join_strings(lines) + +define ep_rt_core_12: + set lines to create_list() + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_scan_channels_major_impl(void) {\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n") + set ok to append_list(lines and " for (int c = 0; c < ep_channel_count; c++) {\n") + set ok to append_list(lines and " EpChannel* chan = ep_channel_registry[c];\n") + set ok to append_list(lines and " if (!chan || chan->size <= 0) continue;\n") + set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") + set ok to append_list(lines and " for (long long j = 0; j < chan->size; j++) {\n") + set ok to append_list(lines and " long long idx = (chan->head + j) % chan->capacity;\n") + set ok to append_list(lines and " long long val = chan->data[idx];\n") + set ok to append_list(lines and " if (val != 0) ep_gc_mark_object((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_channel_registry_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_scan_channels_minor_impl(void) {\n") + set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n") + set ok to append_list(lines and " for (int c = 0; c < ep_channel_count; c++) {\n") + set ok to append_list(lines and " EpChannel* chan = ep_channel_registry[c];\n") + set ok to append_list(lines and " if (!chan || chan->size <= 0) continue;\n") + set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") + set ok to append_list(lines and " for (long long j = 0; j < chan->size; j++) {\n") + set ok to append_list(lines and " long long idx = (chan->head + j) % chan->capacity;\n") + set ok to append_list(lines and " long long val = chan->data[idx];\n") + set ok to append_list(lines and " if (val != 0) ep_gc_mark_object_minor((void*)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&ep_channel_registry_mutex);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long create_channel(void) {\n") + set ok to append_list(lines and " EpChannel* chan = malloc(sizeof(EpChannel));\n") + set ok to append_list(lines and " if (!chan) return 0;\n") + set ok to append_list(lines and " chan->capacity = 1024;\n") + set ok to append_list(lines and " chan->data = malloc(chan->capacity * sizeof(long long));\n") + set ok to append_list(lines and " chan->head = 0;\n") + set ok to append_list(lines and " chan->tail = 0;\n") + set ok to append_list(lines and " chan->size = 0;\n") + set ok to append_list(lines and " ep_mutex_init(&chan->mutex);\n") + set ok to append_list(lines and " ep_cond_init(&chan->cond_recv);\n") + set ok to append_list(lines and " ep_cond_init(&chan->cond_send);\n") + set ok to append_list(lines and " ep_register_channel(chan);\n") + set ok to append_list(lines and " return (long long)chan;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long send_channel(long long chan_ptr, long long value) {\n") + set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n") + set ok to append_list(lines and " if (!chan) return 0;\n") + set ok to append_list(lines and " /* Suppress GC during channel operations. The blocking condvar wait\n") + set ok to append_list(lines and " can interleave with GC mark/sweep on another thread, causing\n") + set ok to append_list(lines and " use-after-free when the GC sweeps objects that are live on a\n") + set ok to append_list(lines and " thread currently blocked in send/receive. Channel buffers contain\n") + set ok to append_list(lines and " raw long long values (not GC-tracked pointers), so suppressing\n") + set ok to append_list(lines and " GC here is safe. */\n") + set ok to append_list(lines and " int gc_was_enabled = ep_gc_enabled;\n") + set ok to append_list(lines and " ep_gc_enabled = 0;\n") + set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") + set ok to append_list(lines and " while (chan->size >= chan->capacity) {\n") + set ok to append_list(lines and " ep_cond_wait(&chan->cond_send, &chan->mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " chan->data[chan->tail] = value;\n") + set ok to append_list(lines and " chan->tail = (chan->tail + 1) % chan->capacity;\n") + set ok to append_list(lines and " chan->size += 1;\n") + set ok to append_list(lines and " ep_cond_signal(&chan->cond_recv);\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " ep_gc_enabled = gc_was_enabled;\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long receive_channel(long long chan_ptr) {\n") + set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n") + set ok to append_list(lines and " if (!chan) return 0;\n") + set ok to append_list(lines and " /* Suppress GC during channel receive — same rationale as send_channel */\n") + set ok to append_list(lines and " int gc_was_enabled = ep_gc_enabled;\n") + set ok to append_list(lines and " ep_gc_enabled = 0;\n") + set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") + set ok to append_list(lines and " while (chan->size <= 0) {\n") + set ok to append_list(lines and " ep_cond_wait(&chan->cond_recv, &chan->mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " long long value = chan->data[chan->head];\n") + set ok to append_list(lines and " chan->head = (chan->head + 1) % chan->capacity;\n") + set ok to append_list(lines and " chan->size -= 1;\n") + set ok to append_list(lines and " ep_cond_signal(&chan->cond_send);\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " ep_gc_enabled = gc_was_enabled;\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// Non-blocking receive — returns 1 if data was available, 0 if channel empty\n") + set ok to append_list(lines and "long long channel_try_recv(long long chan_ptr, long long out_ptr) {\n") + set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n") + set ok to append_list(lines and " if (!chan) return 0;\n") + set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") + set ok to append_list(lines and " if (chan->size <= 0) {\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " long long value = chan->data[chan->head];\n") + set ok to append_list(lines and " chan->head = (chan->head + 1) % chan->capacity;\n") + set ok to append_list(lines and " chan->size -= 1;\n") + set ok to append_list(lines and " ep_cond_signal(&chan->cond_send);\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " if (out_ptr) {\n") + set ok to append_list(lines and " *((long long*)out_ptr) = value;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// Check if channel has data without consuming it\n") + set ok to append_list(lines and "long long channel_has_data(long long chan_ptr) {\n") + set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n") + set ok to append_list(lines and " if (!chan) return 0;\n") + set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") + set ok to append_list(lines and " int has = (chan->size > 0) ? 1 : 0;\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " return has;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// Select: wait for any of N channels to have data, with timeout in ms\n") + set ok to append_list(lines and "// channels_list is a list of channel pointers\n") + set ok to append_list(lines and "// Returns index (0-based) of first ready channel, or -1 on timeout\n") + set ok to append_list(lines and "long long channel_select(long long channels_list, long long timeout_ms) {\n") + set ok to append_list(lines and " EpList* list = (EpList*)channels_list;\n") + set ok to append_list(lines and " if (!list || list->length == 0) return -1;\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " ULONGLONG start_tick = GetTickCount64();\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " struct timespec start, now;\n") + set ok to append_list(lines and " clock_gettime(CLOCK_MONOTONIC, &start);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " while (1) {\n") + set ok to append_list(lines and " // Poll all channels\n") + set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") + set ok to append_list(lines and " EpChannel* chan = (EpChannel*)list->data[i];\n") + set ok to append_list(lines and " if (chan) {\n") + set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") + set ok to append_list(lines and " if (chan->size > 0) {\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " return i;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " // Check timeout\n") + return join_strings(lines) + +define ep_rt_core_13: + set lines to create_list() + set ok to append_list(lines and " if (timeout_ms >= 0) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " ULONGLONG now_tick = GetTickCount64();\n") + set ok to append_list(lines and " long long elapsed = (long long)(now_tick - start_tick);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " clock_gettime(CLOCK_MONOTONIC, &now);\n") + set ok to append_list(lines and " long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000;\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " if (elapsed >= timeout_ms) return -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " \n") + set ok to append_list(lines and " // Brief sleep to avoid busy-wait\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " Sleep(1);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " usleep(1000); // 1ms\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and "long long ep_net_connect(const char* host, long long port) {\n") + set ok to append_list(lines and " (void)host; (void)port;\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_listen(long long port) {\n") + set ok to append_list(lines and " (void)port;\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_accept(long long server_fd) {\n") + set ok to append_list(lines and " (void)server_fd;\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_send(long long fd, const char* data) {\n") + set ok to append_list(lines and " (void)fd; (void)data;\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "char* ep_net_recv(long long fd, long long max_len) {\n") + set ok to append_list(lines and " (void)fd; (void)max_len;\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_close(long long fd) {\n") + set ok to append_list(lines and " (void)fd;\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sleep_ms(long long ms) {\n") + set ok to append_list(lines and " struct timespec ts;\n") + set ok to append_list(lines and " ts.tv_sec = ms / 1000;\n") + set ok to append_list(lines and " ts.tv_nsec = (ms % 1000) * 1000000;\n") + set ok to append_list(lines and " nanosleep(&ts, NULL);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_system(long long cmd) {\n") + set ok to append_list(lines and " (void)cmd;\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_play_sound(long long path) {\n") + set ok to append_list(lines and " (void)path;\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlopen(long long path) {\n") + set ok to append_list(lines and " (void)path;\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlsym(long long handle, long long name) {\n") + set ok to append_list(lines and " (void)handle; (void)name;\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlclose(long long handle) {\n") + set ok to append_list(lines and " (void)handle;\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "long long ep_net_connect(const char* host, long long port) {\n") + set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n") + set ok to append_list(lines and " if (sockfd < 0) return -1;\n") + set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n") + set ok to append_list(lines and " if (!server) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " closesocket(sockfd);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") + set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") + set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") + set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n") + set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n") + set ok to append_list(lines and " closesocket(sockfd);\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " // Bounded connect: an unreachable peer must not block ~75s on the OS SYN\n") + set ok to append_list(lines and " // timeout (this stalled node startup). Non-blocking connect + 5s select, then\n") + set ok to append_list(lines and " // restore blocking mode for the rest of the session.\n") + set ok to append_list(lines and " int _ep_flags = fcntl(sockfd, F_GETFL, 0);\n") + set ok to append_list(lines and " fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK);\n") + set ok to append_list(lines and " int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));\n") + set ok to append_list(lines and " if (_ep_cr < 0) {\n") + set ok to append_list(lines and " if (errno != EINPROGRESS) { close(sockfd); return -1; }\n") + set ok to append_list(lines and " fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset);\n") + set ok to append_list(lines and " struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0;\n") + set ok to append_list(lines and " int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv);\n") + set ok to append_list(lines and " if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error\n") + set ok to append_list(lines and " int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err);\n") + set ok to append_list(lines and " if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) {\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " fcntl(sockfd, F_SETFL, _ep_flags);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " return sockfd;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_listen(long long port) {\n") + set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n") + set ok to append_list(lines and " if (sockfd < 0) return -1;\n") + set ok to append_list(lines and " int opt = 1;\n") + set ok to append_list(lines and " setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n") + set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") + set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") + set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") + set ok to append_list(lines and " serv_addr.sin_addr.s_addr = INADDR_ANY;\n") + set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") + set ok to append_list(lines and " if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " closesocket(sockfd);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and " }\n") + return join_strings(lines) + +define ep_rt_core_14: + set lines to create_list() + set ok to append_list(lines and " if (listen(sockfd, 10) < 0) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " closesocket(sockfd);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " return -1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return sockfd;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_accept(long long server_fd) {\n") + set ok to append_list(lines and " struct sockaddr_in cli_addr;\n") + set ok to append_list(lines and " socklen_t clilen = sizeof(cli_addr);\n") + set ok to append_list(lines and " int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);\n") + set ok to append_list(lines and " if (newsockfd >= 0) {\n") + set ok to append_list(lines and " /* Bound how long a single recv/send may block so a slow or silent\n") + set ok to append_list(lines and " client cannot pin a handler thread forever (slowloris). */\n") + set ok to append_list(lines and " struct timeval tv;\n") + set ok to append_list(lines and " tv.tv_sec = 30;\n") + set ok to append_list(lines and " tv.tv_usec = 0;\n") + set ok to append_list(lines and " setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));\n") + set ok to append_list(lines and " setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return newsockfd;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_send(long long fd, const char* data) {\n") + set ok to append_list(lines and " if (!data) return 0;\n") + set ok to append_list(lines and " /* send() may write fewer bytes than requested (partial write under load/\n") + set ok to append_list(lines and " backpressure). A single send() therefore silently truncated large IPC\n") + set ok to append_list(lines and " responses, cutting agent replies mid-stream. Loop until all bytes are sent. */\n") + set ok to append_list(lines and " size_t total = strlen(data);\n") + set ok to append_list(lines and " size_t off = 0;\n") + set ok to append_list(lines and " while (off < total) {\n") + set ok to append_list(lines and " ssize_t n = send((int)fd, data + off, total - off, 0);\n") + set ok to append_list(lines and " if (n <= 0) break;\n") + set ok to append_list(lines and " off += (size_t)n;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return (long long)off;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "char* ep_net_recv(long long fd, long long max_len) {\n") + set ok to append_list(lines and " char* buf = malloc(max_len + 1);\n") + set ok to append_list(lines and " if (!buf) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " int n = recv((int)fd, buf, (int)max_len, 0);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " ssize_t n = recv((int)fd, buf, max_len, 0);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " if (n < 0) n = 0;\n") + set ok to append_list(lines and " buf[n] = '\\0';\n") + set ok to append_list(lines and " return buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_net_close(long long fd) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " return closesocket((int)fd);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " return close((int)fd);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sleep_ms(long long ms) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " Sleep((DWORD)ms);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " usleep((useconds_t)(ms * 1000));\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_system(long long cmd) {\n") + set ok to append_list(lines and " return (long long)system((const char*)cmd);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_play_sound(long long path) {\n") + set ok to append_list(lines and " char cmd[512];\n") + set ok to append_list(lines and " snprintf(cmd, sizeof(cmd), \"afplay '%s' &\", (const char*)path);\n") + set ok to append_list(lines and " return (long long)system(cmd);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Dynamic Library Loading (FFI) ========== */\n") + set ok to append_list(lines and "#ifndef _WIN32\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlopen(long long path) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " HMODULE h = LoadLibraryA((const char*)path);\n") + set ok to append_list(lines and " return (long long)h;\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " const char* p = (const char*)path;\n") + set ok to append_list(lines and " void* handle = dlopen(p, RTLD_LAZY);\n") + set ok to append_list(lines and " return (long long)handle;\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlsym(long long handle, long long name) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name);\n") + set ok to append_list(lines and " return (long long)sym;\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " void* sym = dlsym((void*)handle, (const char*)name);\n") + set ok to append_list(lines and " return (long long)sym;\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlclose(long long handle) {\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " return (long long)FreeLibrary((HMODULE)handle);\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " return (long long)dlclose((void*)handle);\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Call a function pointer with 0..6 arguments.\n") + set ok to append_list(lines and " These are type-punned through long long — the C calling convention\n") + set ok to append_list(lines and " makes this work for integer and pointer arguments. */\n") + set ok to append_list(lines and "typedef long long (*ep_fn0)(void);\n") + set ok to append_list(lines and "typedef long long (*ep_fn1)(long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn2)(long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn3)(long long, long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn4)(long long, long long, long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn5)(long long, long long, long long, long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long);\n") + set ok to append_list(lines and "typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlcall0(long long fptr) {\n") + set ok to append_list(lines and " return ((ep_fn0)fptr)();\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall1(long long fptr, long long a0) {\n") + set ok to append_list(lines and " return ((ep_fn1)fptr)(a0);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall2(long long fptr, long long a0, long long a1) {\n") + set ok to append_list(lines and " return ((ep_fn2)fptr)(a0, a1);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {\n") + set ok to append_list(lines and " return ((ep_fn3)fptr)(a0, a1, a2);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n") + set ok to append_list(lines and " return ((ep_fn4)fptr)(a0, a1, a2, a3);\n") + return join_strings(lines) + +define ep_rt_core_15: + set lines to create_list() + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n") + set ok to append_list(lines and " return ((ep_fn5)fptr)(a0, a1, a2, a3, a4);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {\n") + set ok to append_list(lines and " return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) {\n") + set ok to append_list(lines and " return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) {\n") + set ok to append_list(lines and " return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) {\n") + set ok to append_list(lines and " return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) {\n") + set ok to append_list(lines and " return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Float FFI: ep_dlcall_f* ========== */\n") + set ok to append_list(lines and "/* For calling C functions that accept/return double values.\n") + set ok to append_list(lines and " Arguments are passed as long long (bit-punned doubles).\n") + set ok to append_list(lines and " Return value is a double bit-punned back to long long.\n") + set ok to append_list(lines and " Use ep_double_to_bits() / ep_bits_to_double() to convert. */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef union { long long i; double f; } ep_float_bits;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static inline double ep_ll_to_double(long long v) {\n") + set ok to append_list(lines and " ep_float_bits u; u.i = v; return u.f;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "static inline long long ep_double_to_ll(double v) {\n") + set ok to append_list(lines and " ep_float_bits u; u.f = v; return u.i;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Convert between ErnosPlain float representation and raw bits */\n") + set ok to append_list(lines and "long long ep_double_to_bits(long long float_val) {\n") + set ok to append_list(lines and " /* float_val is already an EP Float stored as long long bits */\n") + set ok to append_list(lines and " return float_val;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_bits_to_double(long long bits) {\n") + set ok to append_list(lines and " return bits;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Float function pointer typedefs */\n") + set ok to append_list(lines and "typedef double (*ep_ff0)(void);\n") + set ok to append_list(lines and "typedef double (*ep_ff1)(double);\n") + set ok to append_list(lines and "typedef double (*ep_ff2)(double, double);\n") + set ok to append_list(lines and "typedef double (*ep_ff3)(double, double, double);\n") + set ok to append_list(lines and "typedef double (*ep_ff4)(double, double, double, double);\n") + set ok to append_list(lines and "typedef double (*ep_ff5)(double, double, double, double, double);\n") + set ok to append_list(lines and "typedef double (*ep_ff6)(double, double, double, double, double, double);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Call functions that take doubles and return double */\n") + set ok to append_list(lines and "long long ep_dlcall_f0(long long fptr) {\n") + set ok to append_list(lines and " return ep_double_to_ll(((ep_ff0)fptr)());\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_f1(long long fptr, long long a0) {\n") + set ok to append_list(lines and " return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0)));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_f2(long long fptr, long long a0, long long a1) {\n") + set ok to append_list(lines and " return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) {\n") + set ok to append_list(lines and " return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n") + set ok to append_list(lines and " return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3)));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n") + set ok to append_list(lines and " return ep_double_to_ll(((ep_ff5)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4)));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_f6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {\n") + set ok to append_list(lines and " return ep_double_to_ll(((ep_ff6)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4), ep_ll_to_double(a5)));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Variants that take doubles but return int (for comparison functions etc.) */\n") + set ok to append_list(lines and "typedef long long (*ep_fdi1)(double);\n") + set ok to append_list(lines and "typedef long long (*ep_fdi2)(double, double);\n") + set ok to append_list(lines and "typedef long long (*ep_fdi3)(double, double, double);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_dlcall_fd1(long long fptr, long long a0) {\n") + set ok to append_list(lines and " return ((ep_fdi1)fptr)(ep_ll_to_double(a0));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) {\n") + set ok to append_list(lines and " return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) {\n") + set ok to append_list(lines and " return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "/* ========== End Float FFI ========== */\n") + set ok to append_list(lines and "/* ========== End Dynamic Library Loading ========== */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "unsigned long hash_string(const char* str) {\n") + set ok to append_list(lines and " unsigned long hash = 5381;\n") + set ok to append_list(lines and " int c;\n") + set ok to append_list(lines and " while ((c = *str++)) {\n") + set ok to append_list(lines and " hash = ((hash << 5) + hash) + c;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return hash;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " char* key;\n") + set ok to append_list(lines and " long long value;\n") + set ok to append_list(lines and " int used;\n") + set ok to append_list(lines and "} EpMapEntry;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " EpMapEntry* entries;\n") + set ok to append_list(lines and " long long capacity;\n") + set ok to append_list(lines and " long long size;\n") + set ok to append_list(lines and "} EpMap;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Map value traversal for GC — walks all entries and marks values.\n") + set ok to append_list(lines and " Called by ep_gc_mark_object() via function pointer. */\n") + set ok to append_list(lines and "static void ep_gc_mark_map_values_impl(void* ptr) {\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)ptr;\n") + set ok to append_list(lines and " if (!map || !map->entries) return;\n") + set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n") + set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].value != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)map->entries[i].value);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " /* Also mark keys if they are heap strings */\n") + set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n") + set ok to append_list(lines and " ep_gc_mark_object((void*)map->entries[i].key);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void ep_gc_mark_map_values_minor_impl(void* ptr) {\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)ptr;\n") + set ok to append_list(lines and " if (!map || !map->entries) return;\n") + set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n") + set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].value != 0) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].value);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n") + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].key);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long create_map(void) {\n") + set ok to append_list(lines and " EpMap* map = malloc(sizeof(EpMap));\n") + set ok to append_list(lines and " if (!map) return 0;\n") + set ok to append_list(lines and " map->capacity = 16;\n") + set ok to append_list(lines and " map->size = 0;\n") + set ok to append_list(lines and " map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n") + set ok to append_list(lines and " if (!map->entries) {\n") + return join_strings(lines) + +define ep_rt_core_16: + set lines to create_list() + set ok to append_list(lines and " free(map);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_register(map, EP_OBJ_MAP);\n") + set ok to append_list(lines and " return (long long)map;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void map_resize(EpMap* map, long long new_capacity) {\n") + set ok to append_list(lines and " EpMapEntry* old_entries = map->entries;\n") + set ok to append_list(lines and " long long old_capacity = map->capacity;\n") + set ok to append_list(lines and " map->capacity = new_capacity;\n") + set ok to append_list(lines and " map->entries = calloc(new_capacity, sizeof(EpMapEntry));\n") + set ok to append_list(lines and " map->size = 0;\n") + set ok to append_list(lines and " for (long long i = 0; i < old_capacity; i++) {\n") + set ok to append_list(lines and " if (old_entries[i].used && old_entries[i].key != NULL) {\n") + set ok to append_list(lines and " char* key = old_entries[i].key;\n") + set ok to append_list(lines and " long long value = old_entries[i].value;\n") + set ok to append_list(lines and " unsigned long h = hash_string(key) % new_capacity;\n") + set ok to append_list(lines and " while (map->entries[h].used) {\n") + set ok to append_list(lines and " h = (h + 1) % new_capacity;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " map->entries[h].key = key;\n") + set ok to append_list(lines and " map->entries[h].value = value;\n") + set ok to append_list(lines and " map->entries[h].used = 1;\n") + set ok to append_list(lines and " map->size++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(old_entries);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Convert a key value to a string — handles both string pointers and integers */\n") + set ok to append_list(lines and "static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) {\n") + set ok to append_list(lines and " if (key_val == 0) { buf[0] = '0'; buf[1] = '\\0'; return buf; }\n") + set ok to append_list(lines and " /* Check if value is in plausible pointer range for a string */\n") + set ok to append_list(lines and " if (key_val > 0x100000) {\n") + set ok to append_list(lines and " const char* p = (const char*)(void*)key_val;\n") + set ok to append_list(lines and " unsigned char first = (unsigned char)*p;\n") + set ok to append_list(lines and " if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) {\n") + set ok to append_list(lines and " return p; /* valid string pointer */\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " snprintf(buf, bufsize, \"%lld\", key_val);\n") + set ok to append_list(lines and " return buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long map_insert(long long map_ptr, long long key_val, long long value) {\n") + set ok to append_list(lines and " if (EP_BADPTR(map_ptr)) return 0;\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " char keybuf[32];\n") + set ok to append_list(lines and " const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n") + set ok to append_list(lines and " if (!map) return 0;\n") + set ok to append_list(lines and " if (map->size * 2 >= map->capacity) {\n") + set ok to append_list(lines and " map_resize(map, map->capacity * 2);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") + set ok to append_list(lines and " while (map->entries[h].used) {\n") + set ok to append_list(lines and " if (strcmp(map->entries[h].key, key) == 0) {\n") + set ok to append_list(lines and " map->entries[h].value = value;\n") + set ok to append_list(lines and " ep_gc_write_barrier((void*)map_ptr, value);\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " map->entries[h].key = strdup(key);\n") + set ok to append_list(lines and " map->entries[h].value = value;\n") + set ok to append_list(lines and " map->entries[h].used = 1;\n") + set ok to append_list(lines and " map->size++;\n") + set ok to append_list(lines and " ep_gc_write_barrier((void*)map_ptr, value);\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long map_get_val(long long map_ptr, long long key_val) {\n") + set ok to append_list(lines and " if (EP_BADPTR(map_ptr)) return 0;\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " char keybuf[32];\n") + set ok to append_list(lines and " const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n") + set ok to append_list(lines and " if (!map) return 0;\n") + set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") + set ok to append_list(lines and " long long start_h = h;\n") + set ok to append_list(lines and " while (map->entries[h].used) {\n") + set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n") + set ok to append_list(lines and " return map->entries[h].value;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") + set ok to append_list(lines and " if (h == start_h) break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* map_set_str: store a string value (strdup'd copy) under a string key */\n") + set ok to append_list(lines and "long long map_set_str(long long map_ptr, long long key_val, long long str_val) {\n") + set ok to append_list(lines and " /* Store the string pointer as a long long value — same as map_insert */\n") + set ok to append_list(lines and " return map_insert(map_ptr, key_val, str_val);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* map_get_str: retrieve a string value from a map (returns char* as long long) */\n") + set ok to append_list(lines and "long long map_get_str(long long map_ptr, long long key_val) {\n") + set ok to append_list(lines and " /* Same as map_get_val — the stored long long IS a char* pointer */\n") + set ok to append_list(lines and " return map_get_val(map_ptr, key_val);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long map_contains(long long map_ptr, long long key_val) {\n") + set ok to append_list(lines and " if (EP_BADPTR(map_ptr)) return 0;\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " char keybuf[32];\n") + set ok to append_list(lines and " const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n") + set ok to append_list(lines and " if (!map) return 0;\n") + set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") + set ok to append_list(lines and " long long start_h = h;\n") + set ok to append_list(lines and " while (map->entries[h].used) {\n") + set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") + set ok to append_list(lines and " if (h == start_h) break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long map_delete(long long map_ptr, long long key_val) {\n") + set ok to append_list(lines and " if (EP_BADPTR(map_ptr)) return 0;\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " char keybuf[32];\n") + set ok to append_list(lines and " const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n") + set ok to append_list(lines and " if (!map) return 0;\n") + set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n") + set ok to append_list(lines and " long long start_h = h;\n") + set ok to append_list(lines and " while (map->entries[h].used) {\n") + set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n") + set ok to append_list(lines and " free(map->entries[h].key);\n") + set ok to append_list(lines and " map->entries[h].key = NULL;\n") + set ok to append_list(lines and " map->entries[h].value = 0;\n") + set ok to append_list(lines and " map->entries[h].used = 0;\n") + set ok to append_list(lines and " map->size--;\n") + set ok to append_list(lines and " long long next_h = (h + 1) % map->capacity;\n") + set ok to append_list(lines and " while (map->entries[next_h].used) {\n") + set ok to append_list(lines and " char* k = map->entries[next_h].key;\n") + set ok to append_list(lines and " long long v = map->entries[next_h].value;\n") + set ok to append_list(lines and " map->entries[next_h].key = NULL;\n") + set ok to append_list(lines and " map->entries[next_h].value = 0;\n") + set ok to append_list(lines and " map->entries[next_h].used = 0;\n") + set ok to append_list(lines and " map->size--;\n") + set ok to append_list(lines and " map_insert(map_ptr, (long long)k, v);\n") + set ok to append_list(lines and " free(k);\n") + set ok to append_list(lines and " next_h = (next_h + 1) % map->capacity;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") + set ok to append_list(lines and " if (h == start_h) break;\n") + return join_strings(lines) + +define ep_rt_core_17: + set lines to create_list() + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long map_keys(long long map_ptr) {\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " if (!map) return (long long)create_list();\n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n") + set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key) {\n") + set ok to append_list(lines and " append_list(list, (long long)strdup(map->entries[i].key));\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long map_values(long long map_ptr) {\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " if (!map) return (long long)create_list();\n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n") + set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key) {\n") + set ok to append_list(lines and " append_list(list, map->entries[i].value);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long map_size(long long map_ptr) {\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " if (!map) return 0;\n") + set ok to append_list(lines and " return map->size;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long free_map(long long map_ptr) {\n") + set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n") + set ok to append_list(lines and " if (!map) return 0;\n") + set ok to append_list(lines and " /* Skip if already freed (idempotent) */\n") + set ok to append_list(lines and " if (!ep_gc_find(map)) return 0;\n") + set ok to append_list(lines and " ep_gc_unregister(map);\n") + set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n") + set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n") + set ok to append_list(lines and " free(map->entries[i].key);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(map->entries);\n") + set ok to append_list(lines and " free(map);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " long long* data;\n") + set ok to append_list(lines and " long long capacity;\n") + set ok to append_list(lines and " long long head;\n") + set ok to append_list(lines and " long long tail;\n") + set ok to append_list(lines and " long long size;\n") + set ok to append_list(lines and "} EpDeque;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long create_deque(void) {\n") + set ok to append_list(lines and " EpDeque* dq = malloc(sizeof(EpDeque));\n") + set ok to append_list(lines and " if (!dq) return 0;\n") + set ok to append_list(lines and " dq->capacity = 16;\n") + set ok to append_list(lines and " dq->size = 0;\n") + set ok to append_list(lines and " dq->head = 0;\n") + set ok to append_list(lines and " dq->tail = 0;\n") + set ok to append_list(lines and " dq->data = malloc(dq->capacity * sizeof(long long));\n") + set ok to append_list(lines and " if (!dq->data) {\n") + set ok to append_list(lines and " free(dq);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return (long long)dq;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static void deque_resize(EpDeque* dq, long long new_capacity) {\n") + set ok to append_list(lines and " long long* new_data = malloc(new_capacity * sizeof(long long));\n") + set ok to append_list(lines and " for (long long i = 0; i < dq->size; i++) {\n") + set ok to append_list(lines and " new_data[i] = dq->data[(dq->head + i) % dq->capacity];\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(dq->data);\n") + set ok to append_list(lines and " dq->data = new_data;\n") + set ok to append_list(lines and " dq->capacity = new_capacity;\n") + set ok to append_list(lines and " dq->head = 0;\n") + set ok to append_list(lines and " dq->tail = dq->size;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long deque_push_back(long long dq_ptr, long long value) {\n") + set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") + set ok to append_list(lines and " if (!dq) return 0;\n") + set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n") + set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " dq->data[dq->tail] = value;\n") + set ok to append_list(lines and " dq->tail = (dq->tail + 1) % dq->capacity;\n") + set ok to append_list(lines and " dq->size++;\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long deque_push_front(long long dq_ptr, long long value) {\n") + set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") + set ok to append_list(lines and " if (!dq) return 0;\n") + set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n") + set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;\n") + set ok to append_list(lines and " dq->data[dq->head] = value;\n") + set ok to append_list(lines and " dq->size++;\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long deque_pop_back(long long dq_ptr) {\n") + set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") + set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n") + set ok to append_list(lines and " dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;\n") + set ok to append_list(lines and " long long value = dq->data[dq->tail];\n") + set ok to append_list(lines and " dq->size--;\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long deque_pop_front(long long dq_ptr) {\n") + set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") + set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n") + set ok to append_list(lines and " long long value = dq->data[dq->head];\n") + set ok to append_list(lines and " dq->head = (dq->head + 1) % dq->capacity;\n") + set ok to append_list(lines and " dq->size--;\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long deque_length(long long dq_ptr) {\n") + set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") + set ok to append_list(lines and " if (!dq) return 0;\n") + set ok to append_list(lines and " return dq->size;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long free_deque(long long dq_ptr) {\n") + set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n") + set ok to append_list(lines and " if (!dq) return 0;\n") + set ok to append_list(lines and " free(dq->data);\n") + set ok to append_list(lines and " free(dq);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Filesystem Operations */\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_scan_dir(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " long long list_ptr = create_list();\n") + set ok to append_list(lines and " if (!path) return list_ptr;\n") + set ok to append_list(lines and " DIR* d = opendir(path);\n") + return join_strings(lines) + +define ep_rt_core_18: + set lines to create_list() + set ok to append_list(lines and " if (!d) return list_ptr;\n") + set ok to append_list(lines and " struct dirent* dir;\n") + set ok to append_list(lines and " while ((dir = readdir(d)) != NULL) {\n") + set ok to append_list(lines and " if (strcmp(dir->d_name, \".\") == 0 || strcmp(dir->d_name, \"..\") == 0) {\n") + set ok to append_list(lines and " continue;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " char* name = strdup(dir->d_name);\n") + set ok to append_list(lines and " append_list(list_ptr, (long long)name);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " closedir(d);\n") + set ok to append_list(lines and " return list_ptr;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_copy_file(long long src_val, long long dest_val) {\n") + set ok to append_list(lines and " const char* src = (const char*)src_val;\n") + set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n") + set ok to append_list(lines and " if (!src || !dest) return 0;\n") + set ok to append_list(lines and " FILE* f_src = fopen(src, \"rb\");\n") + set ok to append_list(lines and " if (!f_src) return 0;\n") + set ok to append_list(lines and " FILE* f_dest = fopen(dest, \"wb\");\n") + set ok to append_list(lines and " if (!f_dest) {\n") + set ok to append_list(lines and " fclose(f_src);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " char buf[4096];\n") + set ok to append_list(lines and " size_t n;\n") + set ok to append_list(lines and " while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) {\n") + set ok to append_list(lines and " fwrite(buf, 1, n, f_dest);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " fclose(f_src);\n") + set ok to append_list(lines and " fclose(f_dest);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_delete_file(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " if (!path) return 0;\n") + set ok to append_list(lines and " return remove(path) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_move_file(long long src_val, long long dest_val) {\n") + set ok to append_list(lines and " const char* src = (const char*)src_val;\n") + set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n") + set ok to append_list(lines and " if (!src || !dest) return 0;\n") + set ok to append_list(lines and " return rename(src, dest) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_exists(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " if (!path) return 0;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_is_dir(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " if (!path) return 0;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") + set ok to append_list(lines and " return S_ISDIR(st.st_mode) ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_is_file(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " if (!path) return 0;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") + set ok to append_list(lines and " return S_ISREG(st.st_mode) ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long fs_get_size(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " if (!path) return 0;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") + set ok to append_list(lines and " return (long long)st.st_size;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* HTTP Client */\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and "long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n") + set ok to append_list(lines and " (void)method_val; (void)url_val; (void)headers_val; (void)body_val;\n") + set ok to append_list(lines and " return (long long)strdup(\"Error: HTTP request is not supported on WebAssembly\");\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n") + set ok to append_list(lines and " const char* method = (const char*)method_val;\n") + set ok to append_list(lines and " const char* url = (const char*)url_val;\n") + set ok to append_list(lines and " const char* headers = (const char*)headers_val;\n") + set ok to append_list(lines and " const char* body = (const char*)body_val;\n") + set ok to append_list(lines and " if (!method || !url) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " if (strncmp(url, \"http://\", 7) != 0) {\n") + set ok to append_list(lines and " return (long long)strdup(\"Error: only http:// protocol supported\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " const char* host_start = url + 7;\n") + set ok to append_list(lines and " const char* path_start = strchr(host_start, '/');\n") + set ok to append_list(lines and " char host[256];\n") + set ok to append_list(lines and " char path[1024];\n") + set ok to append_list(lines and " if (path_start) {\n") + set ok to append_list(lines and " size_t host_len = path_start - host_start;\n") + set ok to append_list(lines and " if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;\n") + set ok to append_list(lines and " strncpy(host, host_start, host_len);\n") + set ok to append_list(lines and " host[host_len] = '\\0';\n") + set ok to append_list(lines and " strncpy(path, path_start, sizeof(path) - 1);\n") + set ok to append_list(lines and " path[sizeof(path) - 1] = '\\0';\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " strncpy(host, host_start, sizeof(host) - 1);\n") + set ok to append_list(lines and " host[sizeof(host) - 1] = '\\0';\n") + set ok to append_list(lines and " strcpy(path, \"/\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " int port = 80;\n") + set ok to append_list(lines and " char* colon = strchr(host, ':');\n") + set ok to append_list(lines and " if (colon) {\n") + set ok to append_list(lines and " *colon = '\\0';\n") + set ok to append_list(lines and " port = atoi(colon + 1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n") + set ok to append_list(lines and " if (sockfd < 0) return (long long)strdup(\"Error: socket creation failed\");\n") + set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n") + set ok to append_list(lines and " if (!server) {\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " return (long long)strdup(\"Error: host resolution failed\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") + set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") + set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") + set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n") + set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") + set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " return (long long)strdup(\"Error: connection failed\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " char req[4096];\n") + set ok to append_list(lines and " size_t body_len = body ? strlen(body) : 0;\n") + set ok to append_list(lines and " int req_len = snprintf(req, sizeof(req),\n") + set ok to append_list(lines and " \"%s %s HTTP/1.1\\r\\n\"\n") + set ok to append_list(lines and " \"Host: %s\\r\\n\"\n") + set ok to append_list(lines and " \"Content-Length: %zu\\r\\n\"\n") + set ok to append_list(lines and " \"Connection: close\\r\\n\"\n") + set ok to append_list(lines and " \"%s%s\"\n") + set ok to append_list(lines and " \"\\r\\n\",\n") + set ok to append_list(lines and " method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n") + set ok to append_list(lines and " if (send(sockfd, req, req_len, 0) < 0) {\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " return (long long)strdup(\"Error: send failed\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (body_len > 0) {\n") + set ok to append_list(lines and " if (send(sockfd, body, body_len, 0) < 0) {\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " return (long long)strdup(\"Error: send body failed\");\n") + return join_strings(lines) + +define ep_rt_core_19: + set lines to create_list() + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " size_t resp_cap = 4096;\n") + set ok to append_list(lines and " size_t resp_len = 0;\n") + set ok to append_list(lines and " char* resp = malloc(resp_cap);\n") + set ok to append_list(lines and " if (!resp) {\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " return (long long)strdup(\"\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " char recv_buf[4096];\n") + set ok to append_list(lines and " ssize_t n;\n") + set ok to append_list(lines and " while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {\n") + set ok to append_list(lines and " if (resp_len + n >= resp_cap) {\n") + set ok to append_list(lines and " resp_cap *= 2;\n") + set ok to append_list(lines and " char* new_resp = realloc(resp, resp_cap);\n") + set ok to append_list(lines and " if (!new_resp) {\n") + set ok to append_list(lines and " free(resp);\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " return (long long)strdup(\"Error: memory allocation failed\");\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " resp = new_resp;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " memcpy(resp + resp_len, recv_buf, n);\n") + set ok to append_list(lines and " resp_len += n;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " resp[resp_len] = '\\0';\n") + set ok to append_list(lines and " close(sockfd);\n") + set ok to append_list(lines and " // Strip HTTP headers — return only the body after \\r\\n\\r\\n\n") + set ok to append_list(lines and " char* http_body = strstr(resp, \"\\r\\n\\r\\n\");\n") + set ok to append_list(lines and " if (http_body) {\n") + set ok to append_list(lines and " http_body += 4;\n") + set ok to append_list(lines and " char* result = strdup(http_body);\n") + set ok to append_list(lines and " free(resp);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return (long long)resp;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))\n") + set ok to append_list(lines and "#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n") + set ok to append_list(lines and "#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n") + set ok to append_list(lines and "#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n") + set ok to append_list(lines and "#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n") + set ok to append_list(lines and "#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n") + set ok to append_list(lines and "#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " unsigned char data[64];\n") + set ok to append_list(lines and " unsigned int datalen;\n") + set ok to append_list(lines and " unsigned long long bitlen;\n") + set ok to append_list(lines and " unsigned int state[8];\n") + set ok to append_list(lines and "} EP_SHA256_CTX;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static const unsigned int sha256_k[64] = {\n") + set ok to append_list(lines and " 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n") + set ok to append_list(lines and " 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n") + set ok to append_list(lines and " 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n") + set ok to append_list(lines and " 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n") + set ok to append_list(lines and " 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n") + set ok to append_list(lines and " 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n") + set ok to append_list(lines and " 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n") + set ok to append_list(lines and " 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n") + set ok to append_list(lines and "};\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {\n") + set ok to append_list(lines and " unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n") + set ok to append_list(lines and " for (i = 0, j = 0; i < 16; ++i, j += 4)\n") + set ok to append_list(lines and " m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n") + set ok to append_list(lines and " for ( ; i < 64; ++i)\n") + set ok to append_list(lines and " m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n") + set ok to append_list(lines and " a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];\n") + set ok to append_list(lines and " e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];\n") + set ok to append_list(lines and " for (i = 0; i < 64; ++i) {\n") + set ok to append_list(lines and " t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];\n") + set ok to append_list(lines and " t2 = EP0(a) + MAJ(a,b,c);\n") + set ok to append_list(lines and " h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;\n") + set ok to append_list(lines and " ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_sha256_init(EP_SHA256_CTX *ctx) {\n") + set ok to append_list(lines and " ctx->datalen = 0; ctx->bitlen = 0;\n") + set ok to append_list(lines and " ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;\n") + set ok to append_list(lines and " ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {\n") + set ok to append_list(lines and " for (size_t i = 0; i < len; ++i) {\n") + set ok to append_list(lines and " ctx->data[ctx->datalen] = data[i];\n") + set ok to append_list(lines and " ctx->datalen++;\n") + set ok to append_list(lines and " if (ctx->datalen == 64) {\n") + set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n") + set ok to append_list(lines and " ctx->bitlen += 512;\n") + set ok to append_list(lines and " ctx->datalen = 0;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {\n") + set ok to append_list(lines and " unsigned int i = ctx->datalen;\n") + set ok to append_list(lines and " if (ctx->datalen < 56) {\n") + set ok to append_list(lines and " ctx->data[i++] = 0x80;\n") + set ok to append_list(lines and " while (i < 56) ctx->data[i++] = 0x00;\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " ctx->data[i++] = 0x80;\n") + set ok to append_list(lines and " while (i < 64) ctx->data[i++] = 0x00;\n") + set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n") + set ok to append_list(lines and " memset(ctx->data, 0, 56);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ctx->bitlen += ctx->datalen * 8;\n") + set ok to append_list(lines and " ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;\n") + set ok to append_list(lines and " ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;\n") + set ok to append_list(lines and " ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;\n") + set ok to append_list(lines and " ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;\n") + set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n") + set ok to append_list(lines and " for (i = 0; i < 4; ++i) {\n") + set ok to append_list(lines and " hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "char* ep_sha256(const char* s) {\n") + set ok to append_list(lines and " if (!s) s = \"\";\n") + set ok to append_list(lines and " EP_SHA256_CTX ctx;\n") + set ok to append_list(lines and " ep_sha256_init(&ctx);\n") + set ok to append_list(lines and " ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));\n") + set ok to append_list(lines and " unsigned char hash[32];\n") + set ok to append_list(lines and " ep_sha256_final(&ctx, hash);\n") + set ok to append_list(lines and " char* result = malloc(65);\n") + set ok to append_list(lines and " if (result) {\n") + set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n") + set ok to append_list(lines and " snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " result[64] = '\\0';\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary\n") + set ok to append_list(lines and " safe), so keys/messages containing NUL bytes hash correctly. Returns a\n") + set ok to append_list(lines and " malloc'd 64-char lowercase hex string. */\n") + set ok to append_list(lines and "long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {\n") + return join_strings(lines) + +define ep_rt_core_20: + set lines to create_list() + set ok to append_list(lines and " const unsigned char* key = (const unsigned char*)key_ptr;\n") + set ok to append_list(lines and " const unsigned char* msg = (const unsigned char*)msg_ptr;\n") + set ok to append_list(lines and " size_t klen = (size_t)key_len;\n") + set ok to append_list(lines and " size_t mlen = (size_t)msg_len;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " unsigned char k0[64];\n") + set ok to append_list(lines and " memset(k0, 0, sizeof(k0));\n") + set ok to append_list(lines and " if (klen > 64) {\n") + set ok to append_list(lines and " /* Keys longer than the block size are replaced by their hash. */\n") + set ok to append_list(lines and " EP_SHA256_CTX kc;\n") + set ok to append_list(lines and " ep_sha256_init(&kc);\n") + set ok to append_list(lines and " ep_sha256_update(&kc, key ? key : (const unsigned char*)\"\", klen);\n") + set ok to append_list(lines and " unsigned char kh[32];\n") + set ok to append_list(lines and " ep_sha256_final(&kc, kh);\n") + set ok to append_list(lines and " memcpy(k0, kh, 32);\n") + set ok to append_list(lines and " } else if (key) {\n") + set ok to append_list(lines and " memcpy(k0, key, klen);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " unsigned char ipad[64], opad[64];\n") + set ok to append_list(lines and " for (int i = 0; i < 64; i++) {\n") + set ok to append_list(lines and " ipad[i] = k0[i] ^ 0x36;\n") + set ok to append_list(lines and " opad[i] = k0[i] ^ 0x5c;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " /* inner = H((K0 ^ ipad) || message) */\n") + set ok to append_list(lines and " EP_SHA256_CTX ic;\n") + set ok to append_list(lines and " ep_sha256_init(&ic);\n") + set ok to append_list(lines and " ep_sha256_update(&ic, ipad, 64);\n") + set ok to append_list(lines and " if (msg && mlen) ep_sha256_update(&ic, msg, mlen);\n") + set ok to append_list(lines and " unsigned char inner[32];\n") + set ok to append_list(lines and " ep_sha256_final(&ic, inner);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " /* mac = H((K0 ^ opad) || inner) */\n") + set ok to append_list(lines and " EP_SHA256_CTX oc;\n") + set ok to append_list(lines and " ep_sha256_init(&oc);\n") + set ok to append_list(lines and " ep_sha256_update(&oc, opad, 64);\n") + set ok to append_list(lines and " ep_sha256_update(&oc, inner, 32);\n") + set ok to append_list(lines and " unsigned char mac[32];\n") + set ok to append_list(lines and " ep_sha256_final(&oc, mac);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " char* out = (char*)malloc(65);\n") + set ok to append_list(lines and " if (out) {\n") + set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n") + set ok to append_list(lines and " snprintf(out + (i * 2), 3, \"%02x\", mac[i]);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " out[64] = '\\0';\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return (long long)out;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " unsigned int count[2];\n") + set ok to append_list(lines and " unsigned int state[4];\n") + set ok to append_list(lines and " unsigned char buffer[64];\n") + set ok to append_list(lines and "} EP_MD5_CTX;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))\n") + set ok to append_list(lines and "#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))\n") + set ok to append_list(lines and "#define H(x,y,z) ((x) ^ (y) ^ (z))\n") + set ok to append_list(lines and "#define I(x,y,z) ((y) ^ ((x) | ~(z)))\n") + set ok to append_list(lines and "#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#define FF(a,b,c,d,x,s,ac) { \\\n") + set ok to append_list(lines and " (a) += F((b),(c),(d)) + (x) + (ac); \\\n") + set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") + set ok to append_list(lines and " (a) += (b); \\\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#define GG(a,b,c,d,x,s,ac) { \\\n") + set ok to append_list(lines and " (a) += G((b),(c),(d)) + (x) + (ac); \\\n") + set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") + set ok to append_list(lines and " (a) += (b); \\\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#define HH(a,b,c,d,x,s,ac) { \\\n") + set ok to append_list(lines and " (a) += H((b),(c),(d)) + (x) + (ac); \\\n") + set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") + set ok to append_list(lines and " (a) += (b); \\\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#define II(a,b,c,d,x,s,ac) { \\\n") + set ok to append_list(lines and " (a) += I((b),(c),(d)) + (x) + (ac); \\\n") + set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n") + set ok to append_list(lines and " (a) += (b); \\\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_md5_init(EP_MD5_CTX *ctx) {\n") + set ok to append_list(lines and " ctx->count[0] = ctx->count[1] = 0;\n") + set ok to append_list(lines and " ctx->state[0] = 0x67452301;\n") + set ok to append_list(lines and " ctx->state[1] = 0xefcdab89;\n") + set ok to append_list(lines and " ctx->state[2] = 0x98badcfe;\n") + set ok to append_list(lines and " ctx->state[3] = 0x10325476;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {\n") + set ok to append_list(lines and " unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n") + set ok to append_list(lines and " for (int i = 0, j = 0; i < 16; i++, j += 4)\n") + set ok to append_list(lines and " x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);\n") + set ok to append_list(lines and " FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);\n") + set ok to append_list(lines and " FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);\n") + set ok to append_list(lines and " FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);\n") + set ok to append_list(lines and " GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);\n") + set ok to append_list(lines and " GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);\n") + set ok to append_list(lines and " GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);\n") + set ok to append_list(lines and " HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);\n") + set ok to append_list(lines and " HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);\n") + set ok to append_list(lines and " HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);\n") + set ok to append_list(lines and " II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);\n") + set ok to append_list(lines and " II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);\n") + set ok to append_list(lines and " II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " state[0] += a; state[1] += b; state[2] += c; state[3] += d;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {\n") + set ok to append_list(lines and " unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;\n") + set ok to append_list(lines and " ctx->count[0] += input_len << 3;\n") + set ok to append_list(lines and " if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;\n") + set ok to append_list(lines and " ctx->count[1] += input_len >> 29;\n") + set ok to append_list(lines and " if (input_len >= part_len) {\n") + set ok to append_list(lines and " memcpy(&ctx->buffer[index], input, part_len);\n") + set ok to append_list(lines and " ep_md5_transform(ctx->state, ctx->buffer);\n") + set ok to append_list(lines and " for (i = part_len; i + 63 < input_len; i += 64)\n") + set ok to append_list(lines and " ep_md5_transform(ctx->state, &input[i]);\n") + set ok to append_list(lines and " index = 0;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " memcpy(&ctx->buffer[index], &input[i], input_len - i);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n") + set ok to append_list(lines and " unsigned char bits[8];\n") + set ok to append_list(lines and " bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n") + set ok to append_list(lines and " bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n") + set ok to append_list(lines and " unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n") + set ok to append_list(lines and " unsigned char padding[64];\n") + set ok to append_list(lines and " memset(padding, 0, 64); padding[0] = 0x80;\n") + set ok to append_list(lines and " ep_md5_update(ctx, padding, pad_len);\n") + set ok to append_list(lines and " ep_md5_update(ctx, bits, 8);\n") + set ok to append_list(lines and " for (int i = 0; i < 4; i++) {\n") + set ok to append_list(lines and " digest[i*4] = ctx->state[i];\n") + set ok to append_list(lines and " digest[i*4 + 1] = ctx->state[i] >> 8;\n") + set ok to append_list(lines and " digest[i*4 + 2] = ctx->state[i] >> 16;\n") + set ok to append_list(lines and " digest[i*4 + 3] = ctx->state[i] >> 24;\n") + set ok to append_list(lines and " }\n") + return join_strings(lines) + +define ep_rt_core_21: + set lines to create_list() + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "char* ep_md5(const char* s) {\n") + set ok to append_list(lines and " if (!s) s = \"\";\n") + set ok to append_list(lines and " EP_MD5_CTX ctx;\n") + set ok to append_list(lines and " ep_md5_init(&ctx);\n") + set ok to append_list(lines and " ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));\n") + set ok to append_list(lines and " unsigned char hash[16];\n") + set ok to append_list(lines and " ep_md5_final(&ctx, hash);\n") + set ok to append_list(lines and " char* result = malloc(33);\n") + set ok to append_list(lines and " if (result) {\n") + set ok to append_list(lines and " for (int i = 0; i < 16; i++) {\n") + set ok to append_list(lines and " snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " result[32] = '\\0';\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "char* read_file_content(const char* filepath) {\n") + set ok to append_list(lines and " char mode[3];\n") + set ok to append_list(lines and " mode[0] = 'r';\n") + set ok to append_list(lines and " mode[1] = 'b';\n") + set ok to append_list(lines and " mode[2] = '\\0';\n") + set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n") + set ok to append_list(lines and " if (!f) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n") + set ok to append_list(lines and " long size = ftell(f);\n") + set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n") + set ok to append_list(lines and " char* buf = malloc(size + 1);\n") + set ok to append_list(lines and " if (!buf) {\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " size_t read_bytes = fread(buf, 1, size, f);\n") + set ok to append_list(lines and " buf[read_bytes] = '\\0';\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_length(const char* s) {\n") + set ok to append_list(lines and " if (!s) return 0;\n") + set ok to append_list(lines and " return strlen(s);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long get_character(const char* s, long long index) {\n") + set ok to append_list(lines and " if (!s) return 0;\n") + set ok to append_list(lines and " long long len = strlen(s);\n") + set ok to append_list(lines and " if (index < 0 || index >= len) return 0;\n") + set ok to append_list(lines and " return (unsigned char)s[index];\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long create_list(void) {\n") + set ok to append_list(lines and " EpList* list = malloc(sizeof(EpList));\n") + set ok to append_list(lines and " if (!list) return 0;\n") + set ok to append_list(lines and " list->capacity = 4;\n") + set ok to append_list(lines and " list->length = 0;\n") + set ok to append_list(lines and " list->data = malloc(list->capacity * sizeof(long long));\n") + set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n") + set ok to append_list(lines and " return (long long)list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long get_list_data_ptr(long long list_ptr) {\n") + set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (!list) return 0;\n") + set ok to append_list(lines and " return (long long)list->data;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long append_list(long long list_ptr, long long value) {\n") + set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (!list) return 0;\n") + set ok to append_list(lines and " if (list->length >= list->capacity) {\n") + set ok to append_list(lines and " list->capacity *= 2;\n") + set ok to append_list(lines and " list->data = realloc(list->data, list->capacity * sizeof(long long));\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " list->data[list->length] = value;\n") + set ok to append_list(lines and " list->length += 1;\n") + set ok to append_list(lines and " ep_gc_write_barrier((void*)list_ptr, value);\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long get_list(long long list_ptr, long long index) {\n") + set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (index < 0 || index >= list->length) return 0;\n") + set ok to append_list(lines and " return list->data[index];\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long set_list(long long list_ptr, long long index, long long value) {\n") + set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (index < 0 || index >= list->length) return 0;\n") + set ok to append_list(lines and " list->data[index] = value;\n") + set ok to append_list(lines and " ep_gc_write_barrier((void*)list_ptr, value);\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long length_list(long long list_ptr) {\n") + set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " return list->length;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long free_list(long long list_ptr) {\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (!list) return 0;\n") + set ok to append_list(lines and " /* Skip if already freed (idempotent) */\n") + set ok to append_list(lines and " if (!ep_gc_find(list)) return 0;\n") + set ok to append_list(lines and " ep_gc_unregister(list);\n") + set ok to append_list(lines and " free(list->data);\n") + set ok to append_list(lines and " free(list);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {\n") + set ok to append_list(lines and " EpList* rows = (EpList*)arg;\n") + set ok to append_list(lines and " EpList* row = (EpList*)create_list();\n") + set ok to append_list(lines and " for (int i = 0; i < argc; i++) {\n") + set ok to append_list(lines and " char* val = argv[i] ? strdup(argv[i]) : strdup(\"\");\n") + set ok to append_list(lines and " append_list((long long)row, (long long)val);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " append_list((long long)rows, (long long)row);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long sqlite_get_callback_ptr(long long dummy) {\n") + set ok to append_list(lines and " return (long long)sqlite_list_callback;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* SQLite type-safe wrappers — marshal between int and long long */\n") + set ok to append_list(lines and "#ifdef EP_HAS_SQLITE\n") + set ok to append_list(lines and "typedef struct sqlite3 sqlite3;\n") + set ok to append_list(lines and "int sqlite3_open(const char*, sqlite3**);\n") + set ok to append_list(lines and "int sqlite3_close(sqlite3*);\n") + set ok to append_list(lines and "int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_open(long long filename, long long db_ptr) {\n") + set ok to append_list(lines and " sqlite3* db = NULL;\n") + set ok to append_list(lines and " int rc = sqlite3_open((const char*)filename, &db);\n") + return join_strings(lines) + +define ep_rt_core_22: + set lines to create_list() + set ok to append_list(lines and " if (rc == 0 && db_ptr != 0) {\n") + set ok to append_list(lines and " *((long long*)db_ptr) = (long long)db;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return (long long)rc;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_close(long long db) {\n") + set ok to append_list(lines and " return (long long)sqlite3_close((sqlite3*)db);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) {\n") + set ok to append_list(lines and " return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql,\n") + set ok to append_list(lines and " (int(*)(void*,int,char**,char**))(callback),\n") + set ok to append_list(lines and " (void*)cb_arg, (char**)errmsg_ptr);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Prepared-statement API for parameterized queries (defeats SQL injection). */\n") + set ok to append_list(lines and "typedef struct sqlite3_stmt sqlite3_stmt;\n") + set ok to append_list(lines and "int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);\n") + set ok to append_list(lines and "int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));\n") + set ok to append_list(lines and "int sqlite3_bind_int64(sqlite3_stmt*, int, long long);\n") + set ok to append_list(lines and "int sqlite3_step(sqlite3_stmt*);\n") + set ok to append_list(lines and "int sqlite3_column_count(sqlite3_stmt*);\n") + set ok to append_list(lines and "const unsigned char* sqlite3_column_text(sqlite3_stmt*, int);\n") + set ok to append_list(lines and "long long sqlite3_column_int64(sqlite3_stmt*, int);\n") + set ok to append_list(lines and "int sqlite3_finalize(sqlite3_stmt*);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_prepare_v2(long long db, long long sql) {\n") + set ok to append_list(lines and " sqlite3_stmt* stmt = NULL;\n") + set ok to append_list(lines and " int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL);\n") + set ok to append_list(lines and " if (rc != 0) return 0;\n") + set ok to append_list(lines and " return (long long)stmt;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) {\n") + set ok to append_list(lines and " /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is\n") + set ok to append_list(lines and " a bound parameter, never concatenated into SQL — this is the safe path. */\n") + set ok to append_list(lines and " return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx,\n") + set ok to append_list(lines and " (const char*)value, -1, (void(*)(void*))(intptr_t)-1);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) {\n") + set ok to append_list(lines and " return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_step(long long stmt) {\n") + set ok to append_list(lines and " return (long long)sqlite3_step((sqlite3_stmt*)stmt);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_column_count(long long stmt) {\n") + set ok to append_list(lines and " return (long long)sqlite3_column_count((sqlite3_stmt*)stmt);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_column_text(long long stmt, long long col) {\n") + set ok to append_list(lines and " const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);\n") + set ok to append_list(lines and " if (!t) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " return (long long)strdup((const char*)t);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_column_int(long long stmt, long long col) {\n") + set ok to append_list(lines and " return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sqlite3_finalize(long long stmt) {\n") + set ok to append_list(lines and " return (long long)sqlite3_finalize((sqlite3_stmt*)stmt);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif /* EP_HAS_SQLITE */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "int ep_argc = 0;\n") + set ok to append_list(lines and "char** ep_argv = NULL;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "void init_ep_args(int argc, char** argv) {\n") + set ok to append_list(lines and " ep_argc = argc;\n") + set ok to append_list(lines and " ep_argv = argv;\n") + set ok to append_list(lines and " ep_gc_register_thread((void*)&argc);\n") + set ok to append_list(lines and " /* Wire up channel scanning for GC (defined after EpChannel struct) */\n") + set ok to append_list(lines and " ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl;\n") + set ok to append_list(lines and " ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl;\n") + set ok to append_list(lines and " /* Wire up map value traversal for GC (defined after EpMap struct) */\n") + set ok to append_list(lines and " ep_gc_mark_map_values = ep_gc_mark_map_values_impl;\n") + set ok to append_list(lines and " ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long get_argument_count(void) {\n") + set ok to append_list(lines and " return ep_argc;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "const char* get_argument(long long index) {\n") + set ok to append_list(lines and " if (index < 0 || index >= ep_argc) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return ep_argv[index];\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long write_file_content(const char* filepath, const char* content) {\n") + set ok to append_list(lines and " char mode[3];\n") + set ok to append_list(lines and " mode[0] = 'w';\n") + set ok to append_list(lines and " mode[1] = 'b';\n") + set ok to append_list(lines and " mode[2] = '\\0';\n") + set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n") + set ok to append_list(lines and " if (!f) return 0;\n") + set ok to append_list(lines and " size_t len = strlen(content);\n") + set ok to append_list(lines and " size_t written = fwrite(content, 1, len, f);\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " return written == len ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long run_command(const char* command) {\n") + set ok to append_list(lines and " if (!command) return -1;\n") + set ok to append_list(lines and " return system(command);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "char* substring(const char* s, long long start, long long len) {\n") + set ok to append_list(lines and " if (!s) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " long long total_len = strlen(s);\n") + set ok to append_list(lines and " if (start < 0 || start >= total_len || len <= 0) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " if (start + len > total_len) {\n") + set ok to append_list(lines and " len = total_len - start;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " char* sub = malloc(len + 1);\n") + set ok to append_list(lines and " if (!sub) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " strncpy(sub, s + start, len);\n") + set ok to append_list(lines and " sub[len] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(sub, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return sub;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "char* string_from_list(long long list_ptr) {\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (!list) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") + return join_strings(lines) + +define ep_rt_core_23: + set lines to create_list() + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " char* s = malloc(list->length + 1);\n") + set ok to append_list(lines and " if (!s) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return empty;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") + set ok to append_list(lines and " s[i] = (char)list->data[i];\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " s[list->length] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(s, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return s;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// Inverse of string_from_list: convert a string to a list of its byte values in\n") + set ok to append_list(lines and "// a single O(n) pass (one strlen + one copy). This lets callers iterate a string\n") + set ok to append_list(lines and "// in O(n) total via O(1) get_list, instead of O(n) get_character per index\n") + set ok to append_list(lines and "// (which is O(n^2) over the whole string).\n") + set ok to append_list(lines and "long long string_to_list(const char* s) {\n") + set ok to append_list(lines and " EpList* list = malloc(sizeof(EpList));\n") + set ok to append_list(lines and " if (!list) return 0;\n") + set ok to append_list(lines and " long long len = s ? (long long)strlen(s) : 0;\n") + set ok to append_list(lines and " list->capacity = len > 0 ? len : 4;\n") + set ok to append_list(lines and " list->length = len;\n") + set ok to append_list(lines and " list->data = malloc(list->capacity * sizeof(long long));\n") + set ok to append_list(lines and " if (!list->data) {\n") + set ok to append_list(lines and " list->capacity = 0;\n") + set ok to append_list(lines and " list->length = 0;\n") + set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n") + set ok to append_list(lines and " return (long long)list;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " for (long long i = 0; i < len; i++) {\n") + set ok to append_list(lines and " list->data[i] = (unsigned char)s[i];\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n") + set ok to append_list(lines and " return (long long)list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long pop_list(long long list_ptr) {\n") + set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (!list || list->length <= 0) return 0;\n") + set ok to append_list(lines and " list->length -= 1;\n") + set ok to append_list(lines and " return list->data[list->length];\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long remove_list(long long list_ptr, long long index) {\n") + set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n") + set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") + set ok to append_list(lines and " if (!list || index < 0 || index >= list->length) return 0;\n") + set ok to append_list(lines and " long long removed = list->data[index];\n") + set ok to append_list(lines and " for (long long i = index; i < list->length - 1; i++) {\n") + set ok to append_list(lines and " list->data[i] = list->data[i + 1];\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " list->length -= 1;\n") + set ok to append_list(lines and " return removed;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long display_string(const char* s) {\n") + set ok to append_list(lines and " if (s) puts(s);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== File System Runtime ========== */\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #define mkdir(p, m) _mkdir(p)\n") + set ok to append_list(lines and " #define rmdir _rmdir\n") + set ok to append_list(lines and " #define getcwd _getcwd\n") + set ok to append_list(lines and " #define popen _popen\n") + set ok to append_list(lines and " #define pclose _pclose\n") + set ok to append_list(lines and " #define getpid _getpid\n") + set ok to append_list(lines and " #define setenv(k, v, o) _putenv_s(k, v)\n") + set ok to append_list(lines and " /* Minimal dirent polyfill for Windows */\n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " typedef struct { char d_name[260]; } ep_dirent;\n") + set ok to append_list(lines and " typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR;\n") + set ok to append_list(lines and " static EP_DIR* ep_opendir(const char* p) {\n") + set ok to append_list(lines and " EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR));\n") + set ok to append_list(lines and " char buf[270]; snprintf(buf, sizeof(buf), \"%s\\\\*\", p);\n") + set ok to append_list(lines and " d->hFind = FindFirstFileA(buf, &d->data);\n") + set ok to append_list(lines and " d->first = 1;\n") + set ok to append_list(lines and " return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " static ep_dirent* ep_readdir(EP_DIR* d) {\n") + set ok to append_list(lines and " static ep_dirent ent;\n") + set ok to append_list(lines and " if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; }\n") + set ok to append_list(lines and " if (!FindNextFileA(d->hFind, &d->data)) return NULL;\n") + set ok to append_list(lines and " strcpy(ent.d_name, d->data.cFileName); return &ent;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); }\n") + set ok to append_list(lines and " #define DIR EP_DIR\n") + set ok to append_list(lines and " #define dirent ep_dirent\n") + set ok to append_list(lines and " #define opendir ep_opendir\n") + set ok to append_list(lines and " #define readdir ep_readdir\n") + set ok to append_list(lines and " #define closedir ep_closedir\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and " #include \n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_read_file(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " FILE* f = fopen(path, \"rb\");\n") + set ok to append_list(lines and " if (!f) return (long long)\"\";\n") + set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n") + set ok to append_list(lines and " long size = ftell(f);\n") + set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n") + set ok to append_list(lines and " char* buf = (char*)malloc(size + 1);\n") + set ok to append_list(lines and " fread(buf, 1, size, f);\n") + set ok to append_list(lines and " buf[size] = '\\0';\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_write_file(long long path_ptr, long long content_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " const char* content = (const char*)content_ptr;\n") + set ok to append_list(lines and " FILE* f = fopen(path, \"wb\");\n") + set ok to append_list(lines and " if (!f) return 0;\n") + set ok to append_list(lines and " fputs(content, f);\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_append_file(long long path_ptr, long long content_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " const char* content = (const char*)content_ptr;\n") + set ok to append_list(lines and " FILE* f = fopen(path, \"ab\");\n") + set ok to append_list(lines and " if (!f) return 0;\n") + set ok to append_list(lines and " fputs(content, f);\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_file_exists(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_is_directory(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") + return join_strings(lines) + +define ep_rt_core_24: + set lines to create_list() + set ok to append_list(lines and " return S_ISDIR(st.st_mode) ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_file_size(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " if (stat(path, &st) != 0) return -1;\n") + set ok to append_list(lines and " return (long long)st.st_size;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_list_directory(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " DIR* dir = opendir(path);\n") + set ok to append_list(lines and " if (!dir) return (long long)create_list();\n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " struct dirent* entry;\n") + set ok to append_list(lines and " while ((entry = readdir(dir)) != NULL) {\n") + set ok to append_list(lines and " if (entry->d_name[0] == '.' && (entry->d_name[1] == '\\0' || \n") + set ok to append_list(lines and " (entry->d_name[1] == '.' && entry->d_name[2] == '\\0'))) continue;\n") + set ok to append_list(lines and " char* name = strdup(entry->d_name);\n") + set ok to append_list(lines and " append_list(list, (long long)name);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " closedir(dir);\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_create_directory(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " return mkdir(path, 0755) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_remove_file(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " return remove(path) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_remove_directory(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " return rmdir(path) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_rename_file(long long old_ptr, long long new_ptr) {\n") + set ok to append_list(lines and " return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_copy_file(long long src_ptr, long long dst_ptr) {\n") + set ok to append_list(lines and " const char* src = (const char*)src_ptr;\n") + set ok to append_list(lines and " const char* dst = (const char*)dst_ptr;\n") + set ok to append_list(lines and " FILE* fin = fopen(src, \"rb\");\n") + set ok to append_list(lines and " if (!fin) return 0;\n") + set ok to append_list(lines and " FILE* fout = fopen(dst, \"wb\");\n") + set ok to append_list(lines and " if (!fout) { fclose(fin); return 0; }\n") + set ok to append_list(lines and " char buf[8192];\n") + set ok to append_list(lines and " size_t n;\n") + set ok to append_list(lines and " while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {\n") + set ok to append_list(lines and " fwrite(buf, 1, n, fout);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " fclose(fin);\n") + set ok to append_list(lines and " fclose(fout);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Date/Time Runtime ========== */\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_now_ms(void) {\n") + set ok to append_list(lines and " struct timeval tv;\n") + set ok to append_list(lines and " gettimeofday(&tv, NULL);\n") + set ok to append_list(lines and " return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_now_sec(void) {\n") + set ok to append_list(lines and " return (long long)time(NULL);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_year(long long ts) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " return tm ? tm->tm_year + 1900 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_month(long long ts) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " return tm ? tm->tm_mon + 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_day(long long ts) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " return tm ? tm->tm_mday : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_hour(long long ts) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " return tm ? tm->tm_hour : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_minute(long long ts) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " return tm ? tm->tm_min : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_second(long long ts) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " return tm ? tm->tm_sec : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_time_weekday(long long ts) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " return tm ? tm->tm_wday : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_format_time(long long ts, long long fmt_ptr) {\n") + set ok to append_list(lines and " time_t t = (time_t)ts;\n") + set ok to append_list(lines and " struct tm* tm = localtime(&t);\n") + set ok to append_list(lines and " if (!tm) return (long long)\"\";\n") + set ok to append_list(lines and " char* buf = (char*)malloc(256);\n") + set ok to append_list(lines and " strftime(buf, 256, (const char*)fmt_ptr, tm);\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== OS Runtime ========== */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_getenv(long long name_ptr) {\n") + set ok to append_list(lines and " const char* val = getenv((const char*)name_ptr);\n") + set ok to append_list(lines and " return val ? (long long)val : (long long)\"\";\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_setenv(long long name_ptr, long long val_ptr) {\n") + set ok to append_list(lines and " return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_get_cwd(void) {\n") + set ok to append_list(lines and " char* buf = (char*)malloc(4096);\n") + set ok to append_list(lines and " if (getcwd(buf, 4096)) return (long long)buf;\n") + set ok to append_list(lines and " free(buf);\n") + set ok to append_list(lines and " return (long long)\"\";\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_os_name(void) {\n") + set ok to append_list(lines and " #if defined(__APPLE__)\n") + set ok to append_list(lines and " return (long long)\"macos\";\n") + set ok to append_list(lines and " #elif defined(__linux__)\n") + return join_strings(lines) + +define ep_rt_core_25: + set lines to create_list() + set ok to append_list(lines and " return (long long)\"linux\";\n") + set ok to append_list(lines and " #elif defined(_WIN32)\n") + set ok to append_list(lines and " return (long long)\"windows\";\n") + set ok to append_list(lines and " #else\n") + set ok to append_list(lines and " return (long long)\"unknown\";\n") + set ok to append_list(lines and " #endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_arch_name(void) {\n") + set ok to append_list(lines and " #if defined(__aarch64__) || defined(__arm64__)\n") + set ok to append_list(lines and " return (long long)\"arm64\";\n") + set ok to append_list(lines and " #elif defined(__x86_64__)\n") + set ok to append_list(lines and " return (long long)\"x86_64\";\n") + set ok to append_list(lines and " #elif defined(__i386__)\n") + set ok to append_list(lines and " return (long long)\"x86\";\n") + set ok to append_list(lines and " #else\n") + set ok to append_list(lines and " return (long long)\"unknown\";\n") + set ok to append_list(lines and " #endif\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_exit(long long code) {\n") + set ok to append_list(lines and " exit((int)code);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_get_pid(void) {\n") + set ok to append_list(lines and " return (long long)getpid();\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_get_home_dir(void) {\n") + set ok to append_list(lines and " const char* home = getenv(\"HOME\");\n") + set ok to append_list(lines and " return home ? (long long)home : (long long)\"\";\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and "long long ep_run_command(long long cmd_ptr) {\n") + set ok to append_list(lines and " (void)cmd_ptr;\n") + set ok to append_list(lines and " return (long long)\"Error: running external commands is not supported on WebAssembly\";\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "long long ep_run_command(long long cmd_ptr) {\n") + set ok to append_list(lines and " const char* cmd = (const char*)cmd_ptr;\n") + set ok to append_list(lines and " FILE* fp = popen(cmd, \"r\");\n") + set ok to append_list(lines and " if (!fp) return (long long)\"\";\n") + set ok to append_list(lines and " char* result = (char*)malloc(65536);\n") + set ok to append_list(lines and " size_t total = 0;\n") + set ok to append_list(lines and " char buf[4096];\n") + set ok to append_list(lines and " while (fgets(buf, sizeof(buf), fp)) {\n") + set ok to append_list(lines and " size_t len = strlen(buf);\n") + set ok to append_list(lines and " memcpy(result + total, buf, len);\n") + set ok to append_list(lines and " total += len;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " result[total] = '\\0';\n") + set ok to append_list(lines and " pclose(fp);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== HashMap helpers ========== */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_hash_string(long long s_ptr) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_ptr;\n") + set ok to append_list(lines and " if (!s) return 0;\n") + set ok to append_list(lines and " unsigned long long hash = 5381;\n") + set ok to append_list(lines and " int c;\n") + set ok to append_list(lines and " while ((c = *s++)) {\n") + set ok to append_list(lines and " hash = ((hash << 5) + hash) + c;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return (long long)hash;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_str_equals(long long a_ptr, long long b_ptr) {\n") + set ok to append_list(lines and " if (a_ptr == b_ptr) return 1;\n") + set ok to append_list(lines and " if (!a_ptr || !b_ptr) return 0;\n") + set ok to append_list(lines and " /* If either value looks like a small integer (not a valid heap pointer),\n") + set ok to append_list(lines and " fall back to integer comparison — strcmp would segfault. */\n") + set ok to append_list(lines and " if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0;\n") + set ok to append_list(lines and " return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Sync Primitives ========== */\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and "long long ep_mutex_create(void) {\n") + set ok to append_list(lines and " CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));\n") + set ok to append_list(lines and " InitializeCriticalSection(m);\n") + set ok to append_list(lines and " return (long long)m;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_mutex_lock_fn(long long m) {\n") + set ok to append_list(lines and " EnterCriticalSection((CRITICAL_SECTION*)m);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_mutex_unlock_fn(long long m) {\n") + set ok to append_list(lines and " LeaveCriticalSection((CRITICAL_SECTION*)m);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_mutex_trylock(long long m) {\n") + set ok to append_list(lines and " return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_mutex_destroy(long long m) {\n") + set ok to append_list(lines and " DeleteCriticalSection((CRITICAL_SECTION*)m);\n") + set ok to append_list(lines and " free((void*)m);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "long long ep_mutex_create(void) {\n") + set ok to append_list(lines and " pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));\n") + set ok to append_list(lines and " pthread_mutex_init(m, NULL);\n") + set ok to append_list(lines and " return (long long)m;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_mutex_lock_fn(long long m) {\n") + set ok to append_list(lines and " return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_mutex_unlock_fn(long long m) {\n") + set ok to append_list(lines and " return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_mutex_trylock(long long m) {\n") + set ok to append_list(lines and " return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_mutex_destroy(long long m) {\n") + set ok to append_list(lines and " pthread_mutex_destroy((pthread_mutex_t*)m);\n") + set ok to append_list(lines and " free((void*)m);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and "long long ep_rwlock_create(void) {\n") + set ok to append_list(lines and " SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n") + set ok to append_list(lines and " InitializeSRWLock(rwl);\n") + set ok to append_list(lines and " return (long long)rwl;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_rwlock_read_lock(long long rwl) {\n") + set ok to append_list(lines and " AcquireSRWLockShared((SRWLOCK*)rwl);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_rwlock_write_lock(long long rwl) {\n") + set ok to append_list(lines and " AcquireSRWLockExclusive((SRWLOCK*)rwl);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_rwlock_unlock(long long rwl) {\n") + set ok to append_list(lines and " /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n") + set ok to append_list(lines and " In practice the caller should know which lock was taken.\n") + set ok to append_list(lines and " ReleaseSRWLockExclusive on a shared lock is undefined, but\n") + set ok to append_list(lines and " the runtime guarantees matched lock/unlock pairs. We default\n") + set ok to append_list(lines and " to releasing the exclusive lock; shared unlock is handled\n") + return join_strings(lines) + +define ep_rt_core_26: + set lines to create_list() + set ok to append_list(lines and " by pairing read_lock -> read_unlock if needed later. */\n") + set ok to append_list(lines and " ReleaseSRWLockExclusive((SRWLOCK*)rwl);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_rwlock_destroy(long long rwl) {\n") + set ok to append_list(lines and " /* SRWLOCK has no destroy */\n") + set ok to append_list(lines and " free((void*)rwl);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "long long ep_rwlock_create(void) {\n") + set ok to append_list(lines and " pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));\n") + set ok to append_list(lines and " pthread_rwlock_init(rwl, NULL);\n") + set ok to append_list(lines and " return (long long)rwl;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_rwlock_read_lock(long long rwl) {\n") + set ok to append_list(lines and " return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_rwlock_write_lock(long long rwl) {\n") + set ok to append_list(lines and " return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_rwlock_unlock(long long rwl) {\n") + set ok to append_list(lines and " return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_rwlock_destroy(long long rwl) {\n") + set ok to append_list(lines and " pthread_rwlock_destroy((pthread_rwlock_t*)rwl);\n") + set ok to append_list(lines and " free((void*)rwl);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "#ifdef _MSC_VER\n") + set ok to append_list(lines and "long long ep_atomic_create(long long initial) {\n") + set ok to append_list(lines and " volatile long long* a = (volatile long long*)malloc(sizeof(long long));\n") + set ok to append_list(lines and " InterlockedExchange64(a, initial);\n") + set ok to append_list(lines and " return (long long)a;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_atomic_load(long long a) {\n") + set ok to append_list(lines and " return InterlockedCompareExchange64((volatile long long*)a, 0, 0);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_atomic_store(long long a, long long value) {\n") + set ok to append_list(lines and " InterlockedExchange64((volatile long long*)a, value);\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_atomic_add(long long a, long long delta) {\n") + set ok to append_list(lines and " return InterlockedExchangeAdd64((volatile long long*)a, delta);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_atomic_sub(long long a, long long delta) {\n") + set ok to append_list(lines and " return InterlockedExchangeAdd64((volatile long long*)a, -delta);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_atomic_cas(long long a, long long expected, long long desired) {\n") + set ok to append_list(lines and " long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected);\n") + set ok to append_list(lines and " return (old == expected) ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "long long ep_atomic_create(long long initial) {\n") + set ok to append_list(lines and " long long* a = (long long*)malloc(sizeof(long long));\n") + set ok to append_list(lines and " __atomic_store_n(a, initial, __ATOMIC_SEQ_CST);\n") + set ok to append_list(lines and " return (long long)a;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_atomic_load(long long a) {\n") + set ok to append_list(lines and " return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_atomic_store(long long a, long long value) {\n") + set ok to append_list(lines and " __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST);\n") + set ok to append_list(lines and " return value;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_atomic_add(long long a, long long delta) {\n") + set ok to append_list(lines and " return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_atomic_sub(long long a, long long delta) {\n") + set ok to append_list(lines and " return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_atomic_cas(long long a, long long expected, long long desired) {\n") + set ok to append_list(lines and " long long exp = expected;\n") + set ok to append_list(lines and " return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " pthread_mutex_t mutex;\n") + set ok to append_list(lines and " pthread_cond_t cond;\n") + set ok to append_list(lines and " unsigned count;\n") + set ok to append_list(lines and " unsigned target;\n") + set ok to append_list(lines and " unsigned generation;\n") + set ok to append_list(lines and "} EpBarrier;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_barrier_create(long long count) {\n") + set ok to append_list(lines and " EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier));\n") + set ok to append_list(lines and " pthread_mutex_init(&b->mutex, NULL);\n") + set ok to append_list(lines and " pthread_cond_init(&b->cond, NULL);\n") + set ok to append_list(lines and " b->count = 0;\n") + set ok to append_list(lines and " b->target = (unsigned)count;\n") + set ok to append_list(lines and " b->generation = 0;\n") + set ok to append_list(lines and " return (long long)b;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_barrier_wait(long long bp) {\n") + set ok to append_list(lines and " EpBarrier* b = (EpBarrier*)bp;\n") + set ok to append_list(lines and " pthread_mutex_lock(&b->mutex);\n") + set ok to append_list(lines and " unsigned gen = b->generation;\n") + set ok to append_list(lines and " b->count++;\n") + set ok to append_list(lines and " if (b->count >= b->target) {\n") + set ok to append_list(lines and " b->count = 0;\n") + set ok to append_list(lines and " b->generation++;\n") + set ok to append_list(lines and " pthread_cond_broadcast(&b->cond);\n") + set ok to append_list(lines and " pthread_mutex_unlock(&b->mutex);\n") + set ok to append_list(lines and " return 1; /* serial thread */\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " while (gen == b->generation) {\n") + set ok to append_list(lines and " pthread_cond_wait(&b->cond, &b->mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&b->mutex);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_barrier_destroy(long long bp) {\n") + set ok to append_list(lines and " EpBarrier* b = (EpBarrier*)bp;\n") + set ok to append_list(lines and " pthread_mutex_destroy(&b->mutex);\n") + set ok to append_list(lines and " pthread_cond_destroy(&b->cond);\n") + set ok to append_list(lines and " free(b);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Semaphore via mutex+condvar (portable) */\n") + set ok to append_list(lines and "typedef struct {\n") + set ok to append_list(lines and " pthread_mutex_t mutex;\n") + set ok to append_list(lines and " pthread_cond_t cond;\n") + set ok to append_list(lines and " long long value;\n") + set ok to append_list(lines and "} EpSemaphore;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_semaphore_create(long long initial) {\n") + set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));\n") + set ok to append_list(lines and " pthread_mutex_init(&s->mutex, NULL);\n") + set ok to append_list(lines and " pthread_cond_init(&s->cond, NULL);\n") + set ok to append_list(lines and " s->value = initial;\n") + set ok to append_list(lines and " return (long long)s;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_semaphore_wait(long long sp) {\n") + return join_strings(lines) + +define ep_rt_core_27: + set lines to create_list() + set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n") + set ok to append_list(lines and " pthread_mutex_lock(&s->mutex);\n") + set ok to append_list(lines and " while (s->value <= 0) {\n") + set ok to append_list(lines and " pthread_cond_wait(&s->cond, &s->mutex);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " s->value--;\n") + set ok to append_list(lines and " pthread_mutex_unlock(&s->mutex);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_semaphore_post(long long sp) {\n") + set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n") + set ok to append_list(lines and " pthread_mutex_lock(&s->mutex);\n") + set ok to append_list(lines and " s->value++;\n") + set ok to append_list(lines and " pthread_cond_signal(&s->cond);\n") + set ok to append_list(lines and " pthread_mutex_unlock(&s->mutex);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_semaphore_trywait(long long sp) {\n") + set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n") + set ok to append_list(lines and " pthread_mutex_lock(&s->mutex);\n") + set ok to append_list(lines and " if (s->value > 0) {\n") + set ok to append_list(lines and " s->value--;\n") + set ok to append_list(lines and " pthread_mutex_unlock(&s->mutex);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " pthread_mutex_unlock(&s->mutex);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_semaphore_destroy(long long sp) {\n") + set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n") + set ok to append_list(lines and " pthread_mutex_destroy(&s->mutex);\n") + set ok to append_list(lines and " pthread_cond_destroy(&s->cond);\n") + set ok to append_list(lines and " free(s);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_condvar_create(void) {\n") + set ok to append_list(lines and " pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));\n") + set ok to append_list(lines and " pthread_cond_init(cv, NULL);\n") + set ok to append_list(lines and " return (long long)cv;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_condvar_wait(long long cv, long long m) {\n") + set ok to append_list(lines and " return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_condvar_signal(long long cv) {\n") + set ok to append_list(lines and " return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_condvar_broadcast(long long cv) {\n") + set ok to append_list(lines and " return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_condvar_destroy(long long cv) {\n") + set ok to append_list(lines and " pthread_cond_destroy((pthread_cond_t*)cv);\n") + set ok to append_list(lines and " free((void*)cv);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Regex (simple stub — delegates to POSIX regex) ========== */\n") + set ok to append_list(lines and "#include \n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_regex_match(long long pattern_ptr, long long text_ptr) {\n") + set ok to append_list(lines and " regex_t regex;\n") + set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n") + set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n") + set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);\n") + set ok to append_list(lines and " if (ret) return 0;\n") + set ok to append_list(lines and " ret = regexec(®ex, text, 0, NULL, 0);\n") + set ok to append_list(lines and " regfree(®ex);\n") + set ok to append_list(lines and " return ret == 0 ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_regex_find(long long pattern_ptr, long long text_ptr) {\n") + set ok to append_list(lines and " regex_t regex;\n") + set ok to append_list(lines and " regmatch_t match;\n") + set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n") + set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n") + set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n") + set ok to append_list(lines and " if (ret) return (long long)\"\";\n") + set ok to append_list(lines and " ret = regexec(®ex, text, 1, &match, 0);\n") + set ok to append_list(lines and " if (ret != 0) { regfree(®ex); return (long long)\"\"; }\n") + set ok to append_list(lines and " int len = match.rm_eo - match.rm_so;\n") + set ok to append_list(lines and " char* result = (char*)malloc(len + 1);\n") + set ok to append_list(lines and " memcpy(result, text + match.rm_so, len);\n") + set ok to append_list(lines and " result[len] = '\\0';\n") + set ok to append_list(lines and " regfree(®ex);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) {\n") + set ok to append_list(lines and " regex_t regex;\n") + set ok to append_list(lines and " regmatch_t match;\n") + set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n") + set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n") + set ok to append_list(lines and " if (ret) return list;\n") + set ok to append_list(lines and " const char* cursor = text;\n") + set ok to append_list(lines and " while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n") + set ok to append_list(lines and " int len = match.rm_eo - match.rm_so;\n") + set ok to append_list(lines and " char* result = (char*)malloc(len + 1);\n") + set ok to append_list(lines and " memcpy(result, cursor + match.rm_so, len);\n") + set ok to append_list(lines and " result[len] = '\\0';\n") + set ok to append_list(lines and " append_list(list, (long long)result);\n") + set ok to append_list(lines and " cursor += match.rm_eo;\n") + set ok to append_list(lines and " if (match.rm_eo == 0) break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " regfree(®ex);\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) {\n") + set ok to append_list(lines and " /* Simple single-replacement via regex */\n") + set ok to append_list(lines and " regex_t regex;\n") + set ok to append_list(lines and " regmatch_t match;\n") + set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n") + set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n") + set ok to append_list(lines and " const char* repl = (const char*)repl_ptr;\n") + set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n") + set ok to append_list(lines and " if (ret) return text_ptr;\n") + set ok to append_list(lines and " ret = regexec(®ex, text, 1, &match, 0);\n") + set ok to append_list(lines and " if (ret != 0) { regfree(®ex); return text_ptr; }\n") + set ok to append_list(lines and " size_t tlen = strlen(text);\n") + set ok to append_list(lines and " size_t rlen = strlen(repl);\n") + set ok to append_list(lines and " size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen;\n") + set ok to append_list(lines and " char* result = (char*)malloc(new_len + 1);\n") + set ok to append_list(lines and " memcpy(result, text, match.rm_so);\n") + set ok to append_list(lines and " memcpy(result + match.rm_so, repl, rlen);\n") + set ok to append_list(lines and " memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\n") + set ok to append_list(lines and " result[new_len] = '\\0';\n") + set ok to append_list(lines and " regfree(®ex);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_regex_split(long long pattern_ptr, long long text_ptr) {\n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " /* Simple split: find matches and split around them */\n") + set ok to append_list(lines and " regex_t regex;\n") + set ok to append_list(lines and " regmatch_t match;\n") + set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n") + set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n") + set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n") + set ok to append_list(lines and " if (ret) {\n") + set ok to append_list(lines and " append_list(list, text_ptr);\n") + set ok to append_list(lines and " return list;\n") + return join_strings(lines) + +define ep_rt_core_28: + set lines to create_list() + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " const char* cursor = text;\n") + set ok to append_list(lines and " while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n") + set ok to append_list(lines and " int len = match.rm_so;\n") + set ok to append_list(lines and " char* part = (char*)malloc(len + 1);\n") + set ok to append_list(lines and " memcpy(part, cursor, len);\n") + set ok to append_list(lines and " part[len] = '\\0';\n") + set ok to append_list(lines and " append_list(list, (long long)part);\n") + set ok to append_list(lines and " cursor += match.rm_eo;\n") + set ok to append_list(lines and " if (match.rm_eo == 0) break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " char* rest = strdup(cursor);\n") + set ok to append_list(lines and " append_list(list, (long long)rest);\n") + set ok to append_list(lines and " regfree(®ex);\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Base64 ========== */\n") + set ok to append_list(lines and "static const char b64_table[] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_base64_encode(long long data_ptr) {\n") + set ok to append_list(lines and " const unsigned char* data = (const unsigned char*)data_ptr;\n") + set ok to append_list(lines and " size_t len = strlen((const char*)data);\n") + set ok to append_list(lines and " size_t out_len = 4 * ((len + 2) / 3);\n") + set ok to append_list(lines and " char* out = (char*)malloc(out_len + 1);\n") + set ok to append_list(lines and " size_t i, j = 0;\n") + set ok to append_list(lines and " for (i = 0; i < len; i += 3) {\n") + set ok to append_list(lines and " unsigned int n = data[i] << 16;\n") + set ok to append_list(lines and " if (i + 1 < len) n |= data[i+1] << 8;\n") + set ok to append_list(lines and " if (i + 2 < len) n |= data[i+2];\n") + set ok to append_list(lines and " out[j++] = b64_table[(n >> 18) & 63];\n") + set ok to append_list(lines and " out[j++] = b64_table[(n >> 12) & 63];\n") + set ok to append_list(lines and " out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '=';\n") + set ok to append_list(lines and " out[j++] = (i + 2 < len) ? b64_table[n & 63] : '=';\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " out[j] = '\\0';\n") + set ok to append_list(lines and " return (long long)out;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_uuid_v4(void) {\n") + set ok to append_list(lines and " char* uuid = (char*)malloc(37);\n") + set ok to append_list(lines and " unsigned char bytes[16];\n") + set ok to append_list(lines and " ep_secure_random_bytes(bytes, 16);\n") + set ok to append_list(lines and " bytes[6] = (bytes[6] & 0x0F) | 0x40;\n") + set ok to append_list(lines and " bytes[8] = (bytes[8] & 0x3F) | 0x80;\n") + set ok to append_list(lines and " snprintf(uuid, 37, \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n") + set ok to append_list(lines and " bytes[0], bytes[1], bytes[2], bytes[3],\n") + set ok to append_list(lines and " bytes[4], bytes[5], bytes[6], bytes[7],\n") + set ok to append_list(lines and " bytes[8], bytes[9], bytes[10], bytes[11],\n") + set ok to append_list(lines and " bytes[12], bytes[13], bytes[14], bytes[15]);\n") + set ok to append_list(lines and " return (long long)uuid;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long file_read(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " if (!path) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " FILE* f = fopen(path, \"rb\");\n") + set ok to append_list(lines and " if (!f) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n") + set ok to append_list(lines and " long size = ftell(f);\n") + set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n") + set ok to append_list(lines and " char* buf = malloc(size + 1);\n") + set ok to append_list(lines and " if (!buf) { fclose(f); return (long long)strdup(\"\"); }\n") + set ok to append_list(lines and " fread(buf, 1, size, f);\n") + set ok to append_list(lines and " buf[size] = '\\0';\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long file_write(long long path_val, long long content_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " const char* content = (const char*)content_val;\n") + set ok to append_list(lines and " if (!path || !content) return 0;\n") + set ok to append_list(lines and " FILE* f = fopen(path, \"wb\");\n") + set ok to append_list(lines and " if (!f) return 0;\n") + set ok to append_list(lines and " size_t len = strlen(content);\n") + set ok to append_list(lines and " fwrite(content, 1, len, f);\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long file_append(long long path_val, long long content_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " const char* content = (const char*)content_val;\n") + set ok to append_list(lines and " if (!path || !content) return 0;\n") + set ok to append_list(lines and " FILE* f = fopen(path, \"ab\");\n") + set ok to append_list(lines and " if (!f) return 0;\n") + set ok to append_list(lines and " size_t len = strlen(content);\n") + set ok to append_list(lines and " fwrite(content, 1, len, f);\n") + set ok to append_list(lines and " fclose(f);\n") + set ok to append_list(lines and " return 1;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long file_exists(long long path_val) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_val;\n") + set ok to append_list(lines and " if (!path) return 0;\n") + set ok to append_list(lines and " FILE* f = fopen(path, \"r\");\n") + set ok to append_list(lines and " if (f) { fclose(f); return 1; }\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_contains(long long s_val, long long sub_val) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " const char* sub = (const char*)sub_val;\n") + set ok to append_list(lines and " if (!s || !sub) return 0;\n") + set ok to append_list(lines and " return strstr(s, sub) != NULL ? 1 : 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_index_of(long long s_val, long long sub_val) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " const char* sub = (const char*)sub_val;\n") + set ok to append_list(lines and " if (!s || !sub) return -1;\n") + set ok to append_list(lines and " const char* found = strstr(s, sub);\n") + set ok to append_list(lines and " if (!found) return -1;\n") + set ok to append_list(lines and " return (long long)(found - s);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_replace(long long s_val, long long old_val, long long new_val) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " const char* old_str = (const char*)old_val;\n") + set ok to append_list(lines and " const char* new_str = (const char*)new_val;\n") + set ok to append_list(lines and " if (!s || !old_str || !new_str) return (long long)strdup(s ? s : \"\");\n") + set ok to append_list(lines and " size_t old_len = strlen(old_str);\n") + set ok to append_list(lines and " size_t new_len = strlen(new_str);\n") + set ok to append_list(lines and " if (old_len == 0) return (long long)strdup(s);\n") + set ok to append_list(lines and " int count = 0;\n") + set ok to append_list(lines and " const char* p = s;\n") + set ok to append_list(lines and " while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; }\n") + set ok to append_list(lines and " size_t result_len = strlen(s) + count * (new_len - old_len);\n") + set ok to append_list(lines and " char* result = malloc(result_len + 1);\n") + set ok to append_list(lines and " if (!result) return (long long)strdup(s);\n") + set ok to append_list(lines and " char* dst = result;\n") + set ok to append_list(lines and " p = s;\n") + set ok to append_list(lines and " while (*p) {\n") + set ok to append_list(lines and " if (strncmp(p, old_str, old_len) == 0) {\n") + set ok to append_list(lines and " memcpy(dst, new_str, new_len);\n") + set ok to append_list(lines and " dst += new_len;\n") + set ok to append_list(lines and " p += old_len;\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " *dst++ = *p++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " *dst = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Additional String Functions ========== */\n") + set ok to append_list(lines and "#include \n") + return join_strings(lines) + +define ep_rt_core_29: + set lines to create_list() + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_upper(long long s_val) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " if (!s) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " long long len = strlen(s);\n") + set ok to append_list(lines and " char* result = malloc(len + 1);\n") + set ok to append_list(lines and " for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]);\n") + set ok to append_list(lines and " result[len] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_lower(long long s_val) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " if (!s) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " long long len = strlen(s);\n") + set ok to append_list(lines and " char* result = malloc(len + 1);\n") + set ok to append_list(lines and " for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]);\n") + set ok to append_list(lines and " result[len] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_trim(long long s_val) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " if (!s) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " while (*s && isspace((unsigned char)*s)) s++;\n") + set ok to append_list(lines and " long long len = strlen(s);\n") + set ok to append_list(lines and " while (len > 0 && isspace((unsigned char)s[len - 1])) len--;\n") + set ok to append_list(lines and " char* result = malloc(len + 1);\n") + set ok to append_list(lines and " memcpy(result, s, len);\n") + set ok to append_list(lines and " result[len] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_split(long long s_val, long long delim_val) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " const char* delim = (const char*)delim_val;\n") + set ok to append_list(lines and " if (!s || !delim) return create_list();\n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " long long dlen = strlen(delim);\n") + set ok to append_list(lines and " if (dlen == 0) { append_list(list, s_val); return list; }\n") + set ok to append_list(lines and " const char* p = s;\n") + set ok to append_list(lines and " while (1) {\n") + set ok to append_list(lines and " const char* found = strstr(p, delim);\n") + set ok to append_list(lines and " long long partlen = found ? (found - p) : (long long)strlen(p);\n") + set ok to append_list(lines and " char* part = malloc(partlen + 1);\n") + set ok to append_list(lines and " memcpy(part, p, partlen);\n") + set ok to append_list(lines and " part[partlen] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(part, EP_OBJ_STRING);\n") + set ok to append_list(lines and " append_list(list, (long long)part);\n") + set ok to append_list(lines and " if (!found) break;\n") + set ok to append_list(lines and " p = found + dlen;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long char_at(long long s_val, long long index) {\n") + set ok to append_list(lines and " const char* s = (const char*)s_val;\n") + set ok to append_list(lines and " if (!s || index < 0 || index >= (long long)strlen(s)) return 0;\n") + set ok to append_list(lines and " return (unsigned char)s[index];\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long char_from_code(long long code) {\n") + set ok to append_list(lines and " char* result = malloc(2);\n") + set ok to append_list(lines and " result[0] = (char)code;\n") + set ok to append_list(lines and " result[1] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_abs(long long n) {\n") + set ok to append_list(lines and " return n < 0 ? -n : n;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// Auto-convert any value to string for string interpolation\n") + set ok to append_list(lines and "long long ep_auto_to_string(long long val) {\n") + set ok to append_list(lines and " // If the value is 0, return \"0\"\n") + set ok to append_list(lines and " if (val == 0) return (long long)strdup(\"0\");\n") + set ok to append_list(lines and " // Check if val is a GC-tracked string (heap-allocated)\n") + set ok to append_list(lines and " EpGCObject* obj = ep_gc_find((void*)val);\n") + set ok to append_list(lines and " if (obj && obj->kind == EP_OBJ_STRING) {\n") + set ok to append_list(lines and " return val; // It's a known string pointer\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " // Check if val is a static string literal (in .rodata/.data segment)\n") + set ok to append_list(lines and " // These aren't GC-tracked but ARE valid pointers. Use a safe probe:\n") + set ok to append_list(lines and " // only dereference if the address is in a readable memory page.\n") + set ok to append_list(lines and " if (val > 0x100000) {\n") + set ok to append_list(lines and "#if defined(_WIN32)\n") + set ok to append_list(lines and " // Windows: use VirtualQuery to safely probe pointer validity\n") + set ok to append_list(lines and " MEMORY_BASIC_INFORMATION mbi;\n") + set ok to append_list(lines and " if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) {\n") + set ok to append_list(lines and " const char* p = (const char*)(void*)val;\n") + set ok to append_list(lines and " unsigned char first = (unsigned char)*p;\n") + set ok to append_list(lines and " if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n") + set ok to append_list(lines and " return val; // Readable memory, looks like a string\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#elif defined(__APPLE__)\n") + set ok to append_list(lines and " // macOS: use vm_read_overwrite to safely probe\n") + set ok to append_list(lines and " char probe;\n") + set ok to append_list(lines and " vm_size_t sz = 1;\n") + set ok to append_list(lines and " kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz);\n") + set ok to append_list(lines and " if (kr == KERN_SUCCESS) {\n") + set ok to append_list(lines and " unsigned char first = (unsigned char)probe;\n") + set ok to append_list(lines and " if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n") + set ok to append_list(lines and " return val; // Readable memory, looks like a string\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " // Linux: use write() to /dev/null as a safe pointer probe\n") + set ok to append_list(lines and " // write() returns -1 with EFAULT for invalid pointers, no signal\n") + set ok to append_list(lines and " int devnull = open(\"/dev/null\", 1); // O_WRONLY\n") + set ok to append_list(lines and " if (devnull >= 0) {\n") + set ok to append_list(lines and " ssize_t r = write(devnull, (const void*)val, 1);\n") + set ok to append_list(lines and " close(devnull);\n") + set ok to append_list(lines and " if (r == 1) {\n") + set ok to append_list(lines and " const char* p = (const char*)(void*)val;\n") + set ok to append_list(lines and " unsigned char first = (unsigned char)*p;\n") + set ok to append_list(lines and " if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n") + set ok to append_list(lines and " return val;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " // Otherwise, convert integer to string\n") + set ok to append_list(lines and " char* buf = (char*)malloc(32);\n") + set ok to append_list(lines and " snprintf(buf, 32, \"%lld\", val);\n") + set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_random_int(long long min, long long max) {\n") + set ok to append_list(lines and " if (max <= min) return min;\n") + set ok to append_list(lines and " /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n") + set ok to append_list(lines and " unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n") + set ok to append_list(lines and " unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);\n") + set ok to append_list(lines and " unsigned long long r;\n") + set ok to append_list(lines and " do {\n") + set ok to append_list(lines and " ep_secure_random_bytes((unsigned char*)&r, sizeof(r));\n") + set ok to append_list(lines and " } while (r >= limit);\n") + set ok to append_list(lines and " return min + (long long)(r % range);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// JSON built-in functions\n") + set ok to append_list(lines and "static const char* json_skip_ws(const char* p) {\n") + set ok to append_list(lines and " while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n") + set ok to append_list(lines and " return p;\n") + return join_strings(lines) + +define ep_rt_core_30: + set lines to create_list() + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static const char* json_skip_value(const char* p) {\n") + set ok to append_list(lines and " p = json_skip_ws(p);\n") + set ok to append_list(lines and " if (*p == '\"') {\n") + set ok to append_list(lines and " p++;\n") + set ok to append_list(lines and " while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n") + set ok to append_list(lines and " if (*p == '\"') p++;\n") + set ok to append_list(lines and " } else if (*p == '{') {\n") + set ok to append_list(lines and " int depth = 1; p++;\n") + set ok to append_list(lines and " while (*p && depth > 0) {\n") + set ok to append_list(lines and " if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n") + set ok to append_list(lines and " else if (*p == '{') { depth++; p++; }\n") + set ok to append_list(lines and " else if (*p == '}') { depth--; p++; }\n") + set ok to append_list(lines and " else p++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else if (*p == '[') {\n") + set ok to append_list(lines and " int depth = 1; p++;\n") + set ok to append_list(lines and " while (*p && depth > 0) {\n") + set ok to append_list(lines and " if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n") + set ok to append_list(lines and " else if (*p == '[') { depth++; p++; }\n") + set ok to append_list(lines and " else if (*p == ']') { depth--; p++; }\n") + set ok to append_list(lines and " else p++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\\n') p++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return p;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "static const char* json_find_key(const char* json, const char* key) {\n") + set ok to append_list(lines and " const char* p = json_skip_ws(json);\n") + set ok to append_list(lines and " if (*p != '{') return NULL;\n") + set ok to append_list(lines and " p++;\n") + set ok to append_list(lines and " while (*p) {\n") + set ok to append_list(lines and " p = json_skip_ws(p);\n") + set ok to append_list(lines and " if (*p == '}') return NULL;\n") + set ok to append_list(lines and " if (*p != '\"') return NULL;\n") + set ok to append_list(lines and " p++;\n") + set ok to append_list(lines and " const char* ks = p;\n") + set ok to append_list(lines and " while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n") + set ok to append_list(lines and " size_t klen = p - ks;\n") + set ok to append_list(lines and " if (*p == '\"') p++;\n") + set ok to append_list(lines and " p = json_skip_ws(p);\n") + set ok to append_list(lines and " if (*p == ':') p++;\n") + set ok to append_list(lines and " p = json_skip_ws(p);\n") + set ok to append_list(lines and " if (klen == strlen(key) && strncmp(ks, key, klen) == 0) {\n") + set ok to append_list(lines and " return p;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " p = json_skip_value(p);\n") + set ok to append_list(lines and " p = json_skip_ws(p);\n") + set ok to append_list(lines and " if (*p == ',') p++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return NULL;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long json_get_string(long long json_val, long long key_val) {\n") + set ok to append_list(lines and " const char* json = (const char*)json_val;\n") + set ok to append_list(lines and " const char* key = (const char*)key_val;\n") + set ok to append_list(lines and " if (!json || !key) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " const char* val = json_find_key(json, key);\n") + set ok to append_list(lines and " if (!val || *val != '\"') return (long long)strdup(\"\");\n") + set ok to append_list(lines and " val++;\n") + set ok to append_list(lines and " const char* end = val;\n") + set ok to append_list(lines and " while (*end && *end != '\"') { if (*end == '\\\\') end++; end++; }\n") + set ok to append_list(lines and " size_t len = end - val;\n") + set ok to append_list(lines and " char* result = (char*)malloc(len + 1);\n") + set ok to append_list(lines and " // Handle escape sequences\n") + set ok to append_list(lines and " size_t di = 0;\n") + set ok to append_list(lines and " const char* si = val;\n") + set ok to append_list(lines and " while (si < end) {\n") + set ok to append_list(lines and " if (*si == '\\\\' && si + 1 < end) {\n") + set ok to append_list(lines and " si++;\n") + set ok to append_list(lines and " switch (*si) {\n") + set ok to append_list(lines and " case 'n': result[di++] = '\\n'; break;\n") + set ok to append_list(lines and " case 't': result[di++] = '\\t'; break;\n") + set ok to append_list(lines and " case 'r': result[di++] = '\\r'; break;\n") + set ok to append_list(lines and " case '\"': result[di++] = '\"'; break;\n") + set ok to append_list(lines and " case '\\\\': result[di++] = '\\\\'; break;\n") + set ok to append_list(lines and " default: result[di++] = *si; break;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " } else {\n") + set ok to append_list(lines and " result[di++] = *si;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " si++;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " result[di] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long json_get_int(long long json_val, long long key_val) {\n") + set ok to append_list(lines and " const char* json = (const char*)json_val;\n") + set ok to append_list(lines and " const char* key = (const char*)key_val;\n") + set ok to append_list(lines and " if (!json || !key) return 0;\n") + set ok to append_list(lines and " const char* val = json_find_key(json, key);\n") + set ok to append_list(lines and " if (!val) return 0;\n") + set ok to append_list(lines and " return atoll(val);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long json_get_bool(long long json_val, long long key_val) {\n") + set ok to append_list(lines and " const char* json = (const char*)json_val;\n") + set ok to append_list(lines and " const char* key = (const char*)key_val;\n") + set ok to append_list(lines and " if (!json || !key) return 0;\n") + set ok to append_list(lines and " const char* val = json_find_key(json, key);\n") + set ok to append_list(lines and " if (!val) return 0;\n") + set ok to append_list(lines and " if (strncmp(val, \"true\", 4) == 0) return 1;\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// SHA-1 implementation (RFC 3174) for WebSocket handshake\n") + set ok to append_list(lines and "static unsigned int sha1_left_rotate(unsigned int x, int n) {\n") + set ok to append_list(lines and " return (x << n) | (x >> (32 - n));\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sha1(long long data_val) {\n") + set ok to append_list(lines and " const unsigned char* data = (const unsigned char*)data_val;\n") + set ok to append_list(lines and " if (!data) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " size_t len = strlen((const char*)data);\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;\n") + set ok to append_list(lines and " size_t new_len = len + 1;\n") + set ok to append_list(lines and " while (new_len % 64 != 56) new_len++;\n") + set ok to append_list(lines and " unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);\n") + set ok to append_list(lines and " memcpy(msg, data, len);\n") + set ok to append_list(lines and " msg[len] = 0x80;\n") + set ok to append_list(lines and " unsigned long long bits_len = (unsigned long long)len * 8;\n") + set ok to append_list(lines and " for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " for (size_t offset = 0; offset < new_len + 8; offset += 64) {\n") + set ok to append_list(lines and " unsigned int w[80];\n") + set ok to append_list(lines and " for (int i = 0; i < 16; i++) {\n") + set ok to append_list(lines and " w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n") + set ok to append_list(lines and " ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n") + set ok to append_list(lines and " unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n") + set ok to append_list(lines and " for (int i = 0; i < 80; i++) {\n") + set ok to append_list(lines and " unsigned int f, k;\n") + set ok to append_list(lines and " if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }\n") + set ok to append_list(lines and " else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }\n") + set ok to append_list(lines and " else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }\n") + set ok to append_list(lines and " else { f = b ^ c ^ d; k = 0xCA62C1D6; }\n") + set ok to append_list(lines and " unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n") + set ok to append_list(lines and " e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(msg);\n") + set ok to append_list(lines and "\n") + return join_strings(lines) + +define ep_rt_core_31: + set lines to create_list() + set ok to append_list(lines and " // Return Base64-encoded hash directly (for WebSocket handshake)\n") + set ok to append_list(lines and " unsigned char hash[20];\n") + set ok to append_list(lines and " hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;\n") + set ok to append_list(lines and " hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF;\n") + set ok to append_list(lines and " hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF;\n") + set ok to append_list(lines and " hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF;\n") + set ok to append_list(lines and " hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " // Base64 encode the 20-byte hash\n") + set ok to append_list(lines and " size_t b64_len = 4 * ((20 + 2) / 3);\n") + set ok to append_list(lines and " char* result = (char*)malloc(b64_len + 1);\n") + set ok to append_list(lines and " size_t j = 0;\n") + set ok to append_list(lines and " for (size_t bi = 0; bi < 20; bi += 3) {\n") + set ok to append_list(lines and " unsigned int n2 = ((unsigned int)hash[bi]) << 16;\n") + set ok to append_list(lines and " if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8;\n") + set ok to append_list(lines and " if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2];\n") + set ok to append_list(lines and " result[j++] = b64_table[(n2 >> 18) & 0x3F];\n") + set ok to append_list(lines and " result[j++] = b64_table[(n2 >> 12) & 0x3F];\n") + set ok to append_list(lines and " result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '=';\n") + set ok to append_list(lines and " result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '=';\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " result[j] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "// Read exact N bytes from a socket\n") + set ok to append_list(lines and "#ifdef __wasm__\n") + set ok to append_list(lines and "long long ep_net_recv_bytes(long long fd, long long count) {\n") + set ok to append_list(lines and " (void)fd; (void)count;\n") + set ok to append_list(lines and " return (long long)strdup(\"\");\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and "long long ep_net_recv_bytes(long long fd, long long count) {\n") + set ok to append_list(lines and " if (count <= 0) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " char* buf = (char*)malloc(count + 1);\n") + set ok to append_list(lines and "#ifdef _WIN32\n") + set ok to append_list(lines and " int total = 0;\n") + set ok to append_list(lines and " while (total < (int)count) {\n") + set ok to append_list(lines and " int n = recv((int)fd, buf + total, (int)(count - total), 0);\n") + set ok to append_list(lines and " if (n <= 0) break;\n") + set ok to append_list(lines and " total += n;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#else\n") + set ok to append_list(lines and " ssize_t total = 0;\n") + set ok to append_list(lines and " while (total < count) {\n") + set ok to append_list(lines and " ssize_t n = recv((int)fd, buf + total, count - total, 0);\n") + set ok to append_list(lines and " if (n <= 0) break;\n") + set ok to append_list(lines and " total += n;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and " buf[total] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "#endif\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_get_args(void) {\n") + set ok to append_list(lines and " long long list_ptr = create_list();\n") + set ok to append_list(lines and " for (int i = 0; i < ep_argc; i++) {\n") + set ok to append_list(lines and " char* arg_copy = strdup(ep_argv[i]);\n") + set ok to append_list(lines and " ep_gc_register(arg_copy, EP_OBJ_STRING);\n") + set ok to append_list(lines and " append_list(list_ptr, (long long)arg_copy);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return list_ptr;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + return join_strings(lines) + +define ep_rt_builtins_0: + set lines to create_list() + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* Built-in: string concatenation */\n") + set ok to append_list(lines and "long long concat(long long a, long long b) {\n") + set ok to append_list(lines and " const char* sa = (const char*)a;\n") + set ok to append_list(lines and " const char* sb = (const char*)b;\n") + set ok to append_list(lines and " long long la = strlen(sa);\n") + set ok to append_list(lines and " long long lb = strlen(sb);\n") + set ok to append_list(lines and " char* result = malloc(la + lb + 1);\n") + set ok to append_list(lines and " memcpy(result, sa, la);\n") + set ok to append_list(lines and " memcpy(result + la, sb, lb);\n") + set ok to append_list(lines and " result[la + lb] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long int_to_string(long long val) {\n") + set ok to append_list(lines and " char* buf = malloc(32);\n") + set ok to append_list(lines and " snprintf(buf, 32, \"%lld\", val);\n") + set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_int_to_str(long long val) { return int_to_string(val); }\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "typedef struct { char* data; long long len; long long cap; } EpStringBuilder;\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sb_create(long long dummy) {\n") + set ok to append_list(lines and " (void)dummy;\n") + set ok to append_list(lines and " EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));\n") + set ok to append_list(lines and " sb->cap = 256;\n") + set ok to append_list(lines and " sb->len = 0;\n") + set ok to append_list(lines and " sb->data = (char*)malloc(sb->cap);\n") + set ok to append_list(lines and " sb->data[0] = '\\0';\n") + set ok to append_list(lines and " return (long long)sb;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sb_append(long long sb_ptr, long long str_ptr) {\n") + set ok to append_list(lines and " EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n") + set ok to append_list(lines and " const char* s = (const char*)str_ptr;\n") + set ok to append_list(lines and " if (!s) return sb_ptr;\n") + set ok to append_list(lines and " long long slen = strlen(s);\n") + set ok to append_list(lines and " while (sb->len + slen + 1 > sb->cap) {\n") + set ok to append_list(lines and " sb->cap *= 2;\n") + set ok to append_list(lines and " sb->data = (char*)realloc(sb->data, sb->cap);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " memcpy(sb->data + sb->len, s, slen);\n") + set ok to append_list(lines and " sb->len += slen;\n") + set ok to append_list(lines and " sb->data[sb->len] = '\\0';\n") + set ok to append_list(lines and " return sb_ptr;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sb_append_int(long long sb_ptr, long long val) {\n") + set ok to append_list(lines and " char buf[32];\n") + set ok to append_list(lines and " snprintf(buf, sizeof(buf), \"%lld\", val);\n") + set ok to append_list(lines and " return ep_sb_append(sb_ptr, (long long)buf);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sb_to_string(long long sb_ptr) {\n") + set ok to append_list(lines and " EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n") + set ok to append_list(lines and " char* result = (char*)malloc(sb->len + 1);\n") + set ok to append_list(lines and " memcpy(result, sb->data, sb->len + 1);\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " free(sb->data);\n") + set ok to append_list(lines and " free(sb);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_sb_length(long long sb_ptr) {\n") + set ok to append_list(lines and " return ((EpStringBuilder*)sb_ptr)->len;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long str_to_ptr(long long s) { return s; }\n") + set ok to append_list(lines and "long long ptr_to_str(long long p) {\n") + set ok to append_list(lines and " if (p == 0) return (long long)strdup(\"\");\n") + set ok to append_list(lines and " char* copy = strdup((const char*)p);\n") + set ok to append_list(lines and " ep_gc_register(copy, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)copy;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long peek_byte(long long ptr, long long offset) {\n") + set ok to append_list(lines and " return (long long)((unsigned char*)ptr)[offset];\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long poke_byte(long long ptr, long long offset, long long value) {\n") + set ok to append_list(lines and " ((unsigned char*)ptr)[offset] = (unsigned char)value;\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long alloc_bytes(long long size) {\n") + set ok to append_list(lines and " return (long long)calloc((size_t)size, 1);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long free_bytes(long long ptr) {\n") + set ok to append_list(lines and " free((void*)ptr);\n") + set ok to append_list(lines and " return 0;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long list_to_bytes(long long list_ptr) {\n") + set ok to append_list(lines and " long long len = length_list(list_ptr);\n") + set ok to append_list(lines and " unsigned char* buf = (unsigned char*)malloc(len);\n") + set ok to append_list(lines and " for (long long i = 0; i < len; i++) {\n") + set ok to append_list(lines and " buf[i] = (unsigned char)get_list(list_ptr, i);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long bytes_to_list(long long ptr, long long len) {\n") + set ok to append_list(lines and " long long list = create_list();\n") + set ok to append_list(lines and " unsigned char* buf = (unsigned char*)ptr;\n") + set ok to append_list(lines and " for (long long i = 0; i < len; i++) {\n") + set ok to append_list(lines and " append_list(list, (long long)buf[i]);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " return list;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_gc_get_minor_count() {\n") + set ok to append_list(lines and " return ep_gc_minor_count;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_gc_get_major_count() {\n") + set ok to append_list(lines and " return ep_gc_major_count;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "long long ep_gc_get_nursery_count() {\n") + set ok to append_list(lines and " return ep_gc_nursery_count;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long string_to_int(long long s) {\n") + set ok to append_list(lines and " if (s == 0) return 0;\n") + set ok to append_list(lines and " return atoll((const char*)s);\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long read_line() {\n") + set ok to append_list(lines and " char buf[4096];\n") + set ok to append_list(lines and " if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\\0'; }\n") + set ok to append_list(lines and " size_t len = strlen(buf);\n") + set ok to append_list(lines and " if (len > 0 && buf[len-1] == '\\n') buf[len-1] = '\\0';\n") + set ok to append_list(lines and " char* result = strdup(buf);\n") + set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long read_int() {\n") + set ok to append_list(lines and " long long val = 0;\n") + set ok to append_list(lines and " scanf(\"%lld\", &val);\n") + set ok to append_list(lines and " while(getchar() != '\\n');\n") + set ok to append_list(lines and " return val;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long read_float() {\n") + set ok to append_list(lines and " double val = 0.0;\n") + set ok to append_list(lines and " scanf(\"%lf\", &val);\n") + set ok to append_list(lines and " while(getchar() != '\\n');\n") + set ok to append_list(lines and " long long result; memcpy(&result, &val, sizeof(double));\n") + set ok to append_list(lines and " return result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + return join_strings(lines) + +define ep_rt_builtins_1: + set lines to create_list() + set ok to append_list(lines and "long long int_to_float(long long val) {\n") + set ok to append_list(lines and " double d = (double)val;\n") + set ok to append_list(lines and " long long result; memcpy(&result, &d, sizeof(double));\n") + set ok to append_list(lines and " return result;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long float_to_int(long long val) {\n") + set ok to append_list(lines and " double d; memcpy(&d, &val, sizeof(double));\n") + set ok to append_list(lines and " return (long long)d;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + return join_strings(lines) + +define get_shared_runtime_source: + set parts to create_list() + set ok to append_list(parts and ep_rt_core_0()) + set ok to append_list(parts and ep_rt_core_1()) + set ok to append_list(parts and ep_rt_core_2()) + set ok to append_list(parts and ep_rt_core_3()) + set ok to append_list(parts and ep_rt_core_4()) + set ok to append_list(parts and ep_rt_core_5()) + set ok to append_list(parts and ep_rt_core_6()) + set ok to append_list(parts and ep_rt_core_7()) + set ok to append_list(parts and ep_rt_core_8()) + set ok to append_list(parts and ep_rt_core_9()) + set ok to append_list(parts and ep_rt_core_10()) + set ok to append_list(parts and ep_rt_core_11()) + set ok to append_list(parts and ep_rt_core_12()) + set ok to append_list(parts and ep_rt_core_13()) + set ok to append_list(parts and ep_rt_core_14()) + set ok to append_list(parts and ep_rt_core_15()) + set ok to append_list(parts and ep_rt_core_16()) + set ok to append_list(parts and ep_rt_core_17()) + set ok to append_list(parts and ep_rt_core_18()) + set ok to append_list(parts and ep_rt_core_19()) + set ok to append_list(parts and ep_rt_core_20()) + set ok to append_list(parts and ep_rt_core_21()) + set ok to append_list(parts and ep_rt_core_22()) + set ok to append_list(parts and ep_rt_core_23()) + set ok to append_list(parts and ep_rt_core_24()) + set ok to append_list(parts and ep_rt_core_25()) + set ok to append_list(parts and ep_rt_core_26()) + set ok to append_list(parts and ep_rt_core_27()) + set ok to append_list(parts and ep_rt_core_28()) + set ok to append_list(parts and ep_rt_core_29()) + set ok to append_list(parts and ep_rt_core_30()) + set ok to append_list(parts and ep_rt_core_31()) + set ok to append_list(parts and ep_rt_builtins_0()) + set ok to append_list(parts and ep_rt_builtins_1()) + return join_strings(parts) diff --git a/epc.ep b/epc.ep index a6e2610..a29c212 100644 --- a/epc.ep +++ b/epc.ep @@ -2,6 +2,7 @@ import "ep_lexer" import "ep_parser" import "ep_codegen" +import "ep_runtime_gen" define get_file_stem with path: set len to string_length(path) diff --git a/tools/gen_runtime_ep.ep b/tools/gen_runtime_ep.ep new file mode 100644 index 0000000..a276513 --- /dev/null +++ b/tools/gen_runtime_ep.ep @@ -0,0 +1,128 @@ +# gen_runtime_ep.ep — generates ep_runtime_gen.ep from the shared C runtime. +# +# The single-source-of-truth C runtime lives in runtime/ep_runtime.c and +# runtime/ep_builtins.c (embedded by the Rust compiler via include_str!). +# This tool converts those files into an ErnosPlain module of chunked string +# builders so the SELF-HOSTED compiler embeds the exact same runtime text. +# +# Regenerate after any change to runtime/*.c: +# ./target/release/ernos tools/gen_runtime_ep.ep && ./tools/gen_runtime_ep +# +# Output: ep_runtime_gen.ep (committed, imported by epc.ep). + +# Emit one C source line as: set ok to append_list(lines and "\n") +# Escapes: " -> \" \ -> \\ tab -> \t CR skipped. Returns 0. +define emit_line_chars with out and src and line_start and line_end: + set prefix to " set ok to append_list(lines and \"" + set plen to string_length(prefix) + set pi to 0 + repeat while pi < plen: + set ok to append_list(out and get_character(prefix and pi)) + set pi to pi + 1 + set i to line_start + repeat while i < line_end: + set c to get_character(src and i) + if c == 34: + set ok to append_list(out and 92) + set ok to append_list(out and 34) + else: + if c == 92: + set ok to append_list(out and 92) + set ok to append_list(out and 92) + else: + if c == 9: + set ok to append_list(out and 92) + set ok to append_list(out and 116) + else: + if c is not equal to 13: + set ok to append_list(out and c) + set i to i + 1 + # closing: \n") then newline + set ok to append_list(out and 92) + set ok to append_list(out and 110) + set ok to append_list(out and 34) + set ok to append_list(out and 41) + set ok to append_list(out and 10) + return 0 + +# Append every character of a plain string to the output char list. Returns 0. +define emit_str with out and s: + set n to string_length(s) + set i to 0 + repeat while i < n: + set ok to append_list(out and get_character(s and i)) + set i to i + 1 + return 0 + +# Convert one C source file into chunk functions named _ appended to +# `out`, recording each chunk function name (string) into `names`. Returns 0. +define emit_chunks with out and names and src and base: + set src_len to string_length(src) + set pos to 0 + set line_count to 0 + set chunk_index to 0 + set in_chunk to 0 + repeat while pos < src_len: + if in_chunk == 0: + set fname to concat(base and int_to_string(chunk_index)) + set ok to append_list(names and fname) + set ok2 to emit_str(out and "define ") + set ok3 to emit_str(out and fname) + set ok4 to emit_str(out and ":\n set lines to create_list()\n") + set in_chunk to 1 + # find end of current line + set line_start to pos + set scan to 1 + repeat while pos < src_len && scan == 1: + if get_character(src and pos) == 10: + set scan to 0 + else: + set pos to pos + 1 + set ok5 to emit_line_chars(out and src and line_start and pos) + if pos < src_len: + set pos to pos + 1 + set line_count to line_count + 1 + if line_count >= 150: + set ok6 to emit_str(out and " return join_strings(lines)\n\n") + set in_chunk to 0 + set line_count to 0 + set chunk_index to chunk_index + 1 + if in_chunk == 1: + set ok7 to emit_str(out and " return join_strings(lines)\n\n") + set chunk_index to chunk_index + 1 + return 0 + +define main: + set runtime_src to read_file_content("runtime/ep_runtime.c") + set builtins_src to read_file_content("runtime/ep_builtins.c") + if string_length(runtime_src) == 0: + display "error: runtime/ep_runtime.c not found or empty (run from repo root)" + return 1 + if string_length(builtins_src) == 0: + display "error: runtime/ep_builtins.c not found or empty (run from repo root)" + return 1 + + set out to create_list() + set names to create_list() + set h to emit_str(out and "# GENERATED FILE - DO NOT EDIT.\n") + set h2 to emit_str(out and "# Source of truth: runtime/ep_runtime.c + runtime/ep_builtins.c\n") + set h3 to emit_str(out and "# Regenerate: ./target/release/ernos tools/gen_runtime_ep.ep && ./tools/gen_runtime_ep\n\n") + + set r1 to emit_chunks(out and names and runtime_src and "ep_rt_core_") + set r2 to emit_chunks(out and names and builtins_src and "ep_rt_builtins_") + + # get_shared_runtime_source: concatenation of every chunk, in order. + set f to emit_str(out and "define get_shared_runtime_source:\n set parts to create_list()\n") + set n_names to length_list(names) + set i to 0 + repeat while i < n_names: + set f2 to emit_str(out and " set ok to append_list(parts and ") + set f3 to emit_str(out and get_list(names and i)) + set f4 to emit_str(out and "())\n") + set i to i + 1 + set f5 to emit_str(out and " return join_strings(parts)\n") + + set content to string_from_list(out) + set w to write_file_content("ep_runtime_gen.ep" and content) + display concat("wrote ep_runtime_gen.ep with chunks: " and int_to_string(n_names)) + return 0 From 469993e4402206086702f160e1c2b7366ed37736 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:26:03 +0100 Subject: [PATCH 06/51] feat(self-host): f-string interpolation + builtin return-type registry (parity 15->24/54) f-strings were the single largest parity blocker (66 'undeclared identifier f' errors across the suite): the self-hosted lexer had no notion of them and emitted 'f' as a bare identifier. - ep_lexer.ep: new lex_string_body handles all string literals. F-strings are desugared directly in the token stream, mirroring the Rust parser's semantics: f"a{x}b" -> concat("a" and concat(ep_auto_to_string(x) and "b")) via a streaming right-fold. Interpolated expressions are lexed by calling tokenize_source recursively (brace-depth + nested-string aware) and splicing the tokens. Adds \{ and \} escapes. - ep_codegen.ep: register return types for ~70 shared-runtime builtins (string family, math/time, crypto, files, FFI dlcall*, bytes, map/list, GC introspection) so display/type-inference handle their results correctly. Dropped map_has_key (documented in AGENT.md but implemented nowhere). Gates: 3-stage fixpoint byte-identical; parity 15->24/54; run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 77 +++++++++++++++- ep_lexer.ep | 251 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 270 insertions(+), 58 deletions(-) diff --git a/ep_codegen.ep b/ep_codegen.ep index 332f45e..6a543ee 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -260,7 +260,82 @@ define analyze_return_types with state and program: set ok to map_put(keys and values and "fs_get_size" and 1) set ok to map_put(keys and values and "ep_http_request" and 3) set ok to map_put(keys and values and "ep_sleep_ms" and 1) - + # String builtins from the shared runtime (TYPE_DYNSTR = 3 unless noted) + set ok to map_put(keys and values and "concat" and 3) + set ok to map_put(keys and values and "string_concat" and 3) + set ok to map_put(keys and values and "ep_auto_to_string" and 3) + set ok to map_put(keys and values and "int_to_string" and 3) + set ok to map_put(keys and values and "ep_int_to_str" and 3) + set ok to map_put(keys and values and "string_upper" and 3) + set ok to map_put(keys and values and "string_lower" and 3) + set ok to map_put(keys and values and "string_trim" and 3) + set ok to map_put(keys and values and "string_replace" and 3) + set ok to map_put(keys and values and "string_split" and 4) + set ok to map_put(keys and values and "string_contains" and 1) + set ok to map_put(keys and values and "string_index_of" and 1) + set ok to map_put(keys and values and "string_to_int" and 1) + set ok to map_put(keys and values and "char_at" and 3) + set ok to map_put(keys and values and "char_from_code" and 3) + set ok to map_put(keys and values and "ptr_to_str" and 3) + set ok to map_put(keys and values and "str_to_ptr" and 1) + set ok to map_put(keys and values and "float_to_string" and 3) + set ok to map_put(keys and values and "ep_sb_to_string" and 3) + set ok to map_put(keys and values and "ep_sb_create" and 1) + set ok to map_put(keys and values and "ep_sb_append" and 1) + set ok to map_put(keys and values and "ep_sb_append_int" and 1) + # Math / time / system (TYPE_INT = 1) + set ok to map_put(keys and values and "ep_random_int" and 1) + set ok to map_put(keys and values and "ep_abs" and 1) + set ok to map_put(keys and values and "ep_time_ms" and 1) + set ok to map_put(keys and values and "ep_time_now_ms" and 1) + set ok to map_put(keys and values and "ep_time_now_sec" and 1) + set ok to map_put(keys and values and "ep_time_day" and 1) + set ok to map_put(keys and values and "ep_time_month" and 1) + set ok to map_put(keys and values and "ep_time_year" and 1) + set ok to map_put(keys and values and "sleep_ms" and 1) + set ok to map_put(keys and values and "ep_system" and 1) + set ok to map_put(keys and values and "int_to_float" and 1) + set ok to map_put(keys and values and "float_to_int" and 1) + # Crypto (hex/encoded strings) + set ok to map_put(keys and values and "ep_hmac_sha256" and 3) + set ok to map_put(keys and values and "ep_base64_encode" and 3) + set ok to map_put(keys and values and "ep_uuid_v4" and 3) + # Files + set ok to map_put(keys and values and "file_read" and 3) + set ok to map_put(keys and values and "file_write" and 1) + set ok to map_put(keys and values and "file_append" and 1) + set ok to map_put(keys and values and "file_exists" and 1) + set ok to map_put(keys and values and "read_line" and 3) + set ok to map_put(keys and values and "read_int" and 1) + # FFI + set ok to map_put(keys and values and "ep_dlopen" and 1) + set ok to map_put(keys and values and "ep_dlsym" and 1) + set ok to map_put(keys and values and "ep_dlclose" and 1) + set ok to map_put(keys and values and "ep_dlcall0" and 1) + set ok to map_put(keys and values and "ep_dlcall1" and 1) + set ok to map_put(keys and values and "ep_dlcall2" and 1) + set ok to map_put(keys and values and "ep_dlcall3" and 1) + set ok to map_put(keys and values and "ep_dlcall4" and 1) + set ok to map_put(keys and values and "ep_dlcall5" and 1) + set ok to map_put(keys and values and "ep_dlcall6" and 1) + # Lists / maps / bytes + set ok to map_put(keys and values and "remove_list" and 1) + set ok to map_put(keys and values and "map_size" and 1) + set ok to map_put(keys and values and "map_get_str" and 3) + set ok to map_put(keys and values and "map_set_str" and 1) + set ok to map_put(keys and values and "map_keys" and 4) + set ok to map_put(keys and values and "map_values" and 4) + set ok to map_put(keys and values and "alloc_bytes" and 1) + set ok to map_put(keys and values and "free_bytes" and 1) + set ok to map_put(keys and values and "peek_byte" and 1) + set ok to map_put(keys and values and "poke_byte" and 1) + set ok to map_put(keys and values and "list_to_bytes" and 1) + set ok to map_put(keys and values and "bytes_to_list" and 4) + # GC introspection + set ok to map_put(keys and values and "ep_gc_get_minor_count" and 1) + set ok to map_put(keys and values and "ep_gc_get_major_count" and 1) + set ok to map_put(keys and values and "ep_gc_get_nursery_count" and 1) + set externals to get_list(program and 2) set ext_len to length_list(externals) set idx to 0 diff --git a/ep_lexer.ep b/ep_lexer.ep index 5e55d25..a5bb12f 100644 --- a/ep_lexer.ep +++ b/ep_lexer.ep @@ -54,6 +54,186 @@ define match_next_word with source and start_pos and next_word: return 0 +# Lex a string-literal body starting at the opening quote `pos0`, appending the +# resulting token(s) to `tokens`. Plain strings emit one STRING token (26). +# F-strings are desugared IN THE TOKEN STREAM, mirroring the Rust parser: +# f"a{x}b" -> concat("a" and concat(ep_auto_to_string(x) and "b")) +# via a streaming right-fold: each part except the final trailing literal opens +# a frame `concat (` `and`, and the final literal closes every frame. +# Interpolated expressions are lexed by calling tokenize_source recursively and +# splicing the tokens (dropping NEWLINE/INDENT/DEDENT/EOF = 28/29/30/31). +# Returns [new_pos, new_col, err]. +define lex_string_body with source and pos0 and source_len and tokens and current_line and start_col and is_fstring: + set pos to pos0 + 1 + set col to start_col + 1 + set opens to 0 + set str_chars to create_list() + set closed to 0 + set err to 0 + set looping to 1 + repeat while pos < source_len && looping == 1: + set ch to get_character(source and pos) + if ch == 34: # closing '"' + set closed to 1 + set looping to 0 + set pos to pos + 1 + set col to col + 1 + else: + if ch == 123 && is_fstring == 1: # '{' — interpolation begins + set pos to pos + 1 + set col to col + 1 + set lit to string_from_list(str_chars) + set str_chars to create_list() + if string_length(lit) != 0: + set t1 to create_token(27 and "concat" and current_line and col) + 0 + set ok1 to append_list(tokens and t1) + set t2 to create_token(23 and "(" and current_line and col) + 0 + set ok2 to append_list(tokens and t2) + set t3 to create_token(26 and lit and current_line and col) + 0 + set ok3 to append_list(tokens and t3) + set t4 to create_token(11 and "and" and current_line and col) + 0 + set ok4 to append_list(tokens and t4) + set opens to opens + 1 + set t5 to create_token(27 and "concat" and current_line and col) + 0 + set ok5 to append_list(tokens and t5) + set t6 to create_token(23 and "(" and current_line and col) + 0 + set ok6 to append_list(tokens and t6) + set t7 to create_token(27 and "ep_auto_to_string" and current_line and col) + 0 + set ok7 to append_list(tokens and t7) + set t8 to create_token(23 and "(" and current_line and col) + 0 + set ok8 to append_list(tokens and t8) + # extract the expression source up to the matching '}' — + # brace-depth aware, and skips through nested string literals + set depth to 1 + set expr_chars to create_list() + set eloop to 1 + repeat while pos < source_len && eloop == 1: + set ec to get_character(source and pos) + if ec == 34: # nested string inside interpolation + set okq to append_list(expr_chars and ec) + set pos to pos + 1 + set col to col + 1 + set sloop to 1 + repeat while pos < source_len && sloop == 1: + set sc to get_character(source and pos) + set okq2 to append_list(expr_chars and sc) + set pos to pos + 1 + set col to col + 1 + if sc == 34: + set sloop to 0 + else: + if sc == 92: + if pos < source_len: + set qc to get_character(source and pos) + set okq3 to append_list(expr_chars and qc) + set pos to pos + 1 + set col to col + 1 + else: + if ec == 125: # '}' + set depth to depth - 1 + if depth == 0: + set pos to pos + 1 + set col to col + 1 + set eloop to 0 + else: + set oka to append_list(expr_chars and ec) + set pos to pos + 1 + set col to col + 1 + else: + if ec == 123: + set depth to depth + 1 + set okb to append_list(expr_chars and ec) + set pos to pos + 1 + set col to col + 1 + if eloop == 1: + display "Lexer Error: Unterminated interpolation in f-string" + set err to 1 + set looping to 0 + else: + set expr_src to string_from_list(expr_chars) + set expr_tokens to tokenize_source(expr_src) + 0 + if expr_tokens == 0: + display "Lexer Error: Invalid expression inside f-string interpolation" + set err to 1 + set looping to 0 + else: + set et_len to length_list(expr_tokens) + set ei to 0 + repeat while ei < et_len: + set et to get_list(expr_tokens and ei) + set ett to get_token_type(et) + if ett != 28 && ett != 29 && ett != 30 && ett != 31: + set okc to append_list(tokens and et) + set ei to ei + 1 + set t9 to create_token(24 and ")" and current_line and col) + 0 + set ok9 to append_list(tokens and t9) + set t10 to create_token(11 and "and" and current_line and col) + 0 + set ok10 to append_list(tokens and t10) + set opens to opens + 1 + else: + if ch == 92: # '\' escape + set pos to pos + 1 + set col to col + 1 + if pos < source_len: + set esc_ch to get_character(source and pos) + if esc_ch == 110: # 'n' + set oke to append_list(str_chars and 10) + else: + if esc_ch == 116: # 't' + set oke to append_list(str_chars and 9) + else: + if esc_ch == 114: # 'r' + set oke to append_list(str_chars and 13) + else: + if esc_ch == 34: # '"' + set oke to append_list(str_chars and 34) + else: + if esc_ch == 92: # '\' + set oke to append_list(str_chars and 92) + else: + if esc_ch == 123: # '{' + set oke to append_list(str_chars and 123) + else: + if esc_ch == 125: # '}' + set oke to append_list(str_chars and 125) + else: + set oke to append_list(str_chars and 92) + set oke2 to append_list(str_chars and esc_ch) + set pos to pos + 1 + set col to col + 1 + else: + display "Lexer Error: Unterminated string literal at escape sequence" + set err to 1 + set looping to 0 + else: + if ch == 10 || ch == 13: + display "Lexer Error: Unterminated string literal" + set err to 1 + set looping to 0 + else: + set okp to append_list(str_chars and ch) + set pos to pos + 1 + set col to col + 1 + if closed == 0: + if err == 0: + display "Lexer Error: Unterminated string literal" + set err to 1 + if err == 0: + set lit2 to string_from_list(str_chars) + set tokf to create_token(26 and lit2 and current_line and start_col) + 0 + set okf to append_list(tokens and tokf) + if is_fstring == 1: + set ci to 0 + repeat while ci < opens: + set tcp to create_token(24 and ")" and current_line and col) + 0 + set okcp to append_list(tokens and tcp) + set ci to ci + 1 + set res to create_list() + set okr1 to append_list(res and pos) + set okr2 to append_list(res and col) + set okr3 to append_list(res and err) + return res + define tokenize_source with source: set tokens to create_list() set source_len to string_length(source) @@ -377,65 +557,22 @@ define tokenize_source with source: set tok to create_token(tok_type and id_str and current_line and current_col - id_len) + 0 set ok to append_list(tokens and tok) else: - if c == 34: # String Literal '"' + if c == 34: # String Literal (plain or f-string) set start_col to current_col - set pos to pos + 1 - set current_col to current_col + 1 - set str_chars to create_list() - set str_loop to 1 - set closed to 0 - repeat while pos < source_len && str_loop == 1: - set ch to get_character(source and pos) - if ch == 34: # '"' - set closed to 1 - set str_loop to 0 - set pos to pos + 1 - set current_col to current_col + 1 - else: - if ch == 92: # '\' - set pos to pos + 1 - set current_col to current_col + 1 - if pos < source_len: - set esc_ch to get_character(source and pos) - if esc_ch == 110: # 'n' - set ok to append_list(str_chars and 10) - else: - if esc_ch == 116: # 't' - set ok to append_list(str_chars and 9) - else: - if esc_ch == 114: # 'r' - set ok to append_list(str_chars and 13) - else: - if esc_ch == 34: # '"' - set ok to append_list(str_chars and 34) - else: - if esc_ch == 92: # '\' - set ok to append_list(str_chars and 92) - else: - # Push raw backslash and the other character - set ok to append_list(str_chars and 92) - set ok to append_list(str_chars and esc_ch) - set pos to pos + 1 - set current_col to current_col + 1 - else: - display "Lexer Error: Unterminated string literal at escape sequence" - set str_loop to 0 - else: - if ch == 10 || ch == 13: - display "Lexer Error: Unterminated string literal" - set str_loop to 0 - else: - set ok to append_list(str_chars and ch) - set pos to pos + 1 - set current_col to current_col + 1 - - if closed == 0: - display "Lexer Error: Unterminated string literal" + # f-string: the previous token is the bare identifier `f` + set is_fstring to 0 + set tok_count to length_list(tokens) + if tok_count > 0: + set last_tok to get_list(tokens and tok_count - 1) + if get_token_type(last_tok) == 27: + if "f" equals get_token_value(last_tok): + set is_fstring to 1 + set popped to pop_list(tokens) + set sres to lex_string_body(source and pos and source_len and tokens and current_line and start_col and is_fstring) + 0 + set pos to get_list(sres and 0) + set current_col to get_list(sres and 1) + if get_list(sres and 2) == 1: return 0 - - set str_val to string_from_list(str_chars) - set tok to create_token(26 and str_val and current_line and start_col) + 0 # TOK_STRING_LITERAL = 26 - set ok to append_list(tokens and tok) else: # Parse Multi-character or single-character symbols # Check lookahead for double symbols: == (18), != (19), && (20), || (21) From 91a5b99d8e417a3019b7a18f04fbe4c2b40f9758 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:34:45 +0100 Subject: [PATCH 07/51] fix(self-host): '_' locals, builtin-shadowing skip, sqlite/crypto link flags (parity 24->26/54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - is_global_var misclassified '_' (no lowercase letters) as an ALL_CAPS global constant, so 'set _ to ...' emitted an assignment to an undeclared C identifier. Globals now additionally require an uppercase letter. - User/imported .ep functions that shadow a C-runtime builtin (e.g. stdlib string.ep wrappers vs the shared runtime's string_length) are no longer emitted — the builtin wins, mirroring the Rust compiler's builtin_c_funcs skip. Registry snapshot stored as state slot 10 (builtin_count). - epc's clang command now passes -DEP_HAS_SQLITE with -lsqlite3 (the runtime's sqlite wrappers are ifdef-gated) and OpenSSL flags for crypto.ep. Gates: fixpoint byte-identical; parity 24->26/54; run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 73 +++++++++++++++++++++++++++++++++++++-------------- epc.ep | 11 ++++++-- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/ep_codegen.ep b/ep_codegen.ep index 6a543ee..bed693b 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -149,8 +149,22 @@ define create_codegen_state: set ok to append_list(state and 0) # spawn_index (7) set ok to append_list(state and (create_list() + 0)) # current_borrowed_keys (8) set ok to append_list(state and (create_list() + 0)) # current_borrowed_values (9) + set ok to append_list(state and 0) # builtin_count (10): entries in func_keys[0..n) that are C-runtime builtins return state +# A user/imported .ep function whose name matches a C-runtime builtin must NOT +# be emitted — the C builtin wins (mirrors the Rust compiler's builtin_c_funcs +# skip). Builtins occupy the first builtin_count slots of func_keys. +define is_builtin_c_func with state and name: + set keys to get_list(state and 3) + set n to get_list(state and 10) + set idx to 0 + repeat while idx < n: + if name equals get_list(keys and idx): + return 1 + set idx to idx + 1 + return 0 + define get_codegen_borrowed_keys with state: return get_list(state and 8) @@ -336,6 +350,10 @@ define analyze_return_types with state and program: set ok to map_put(keys and values and "ep_gc_get_major_count" and 1) set ok to map_put(keys and values and "ep_gc_get_nursery_count" and 1) + # Everything registered above is a C-runtime builtin; user/imported + # functions are appended after this point (see is_builtin_c_func). + set ok to set_list(state and 10 and length_list(keys)) + set externals to get_list(program and 2) set ext_len to length_list(externals) set idx to 0 @@ -493,16 +511,23 @@ define infer_type with state and expr and var_keys and var_values: # Variable offset collection is no longer needed for C transpilation define is_global_var with name: + # A global constant is ALL_CAPS: no lowercase letters AND at least one + # uppercase letter. Names like "_" (the discard variable) are ordinary + # locals — without the uppercase requirement they were misclassified as + # globals and never declared. set len to string_length(name) if len == 0: return 0 + set has_upper to 0 set idx to 0 repeat while idx < len: set ch to get_character(name and idx) if ch >= 97 && ch <= 122: return 0 + if ch >= 65 && ch <= 90: + set has_upper to 1 set idx to idx + 1 - return 1 + return has_upper define cg_string_contains with s and sub: set len to string_length(s) @@ -2203,21 +2228,22 @@ define generate_c with program and is_test_mode: repeat while idx < ext_len: set ext to get_list(externals and idx) set name to get_list(ext and 1) - set params to get_list(ext and 2) - set p_len to length_list(params) - - set proto to "long long " - set proto to string_concat(proto and name) - set proto to string_concat(proto and "(") - - set p_i to 0 - repeat while p_i < p_len: - set proto to string_concat(proto and "long long") - if p_i < p_len - 1: - set proto to string_concat(proto and ", ") - set p_i to p_i + 1 - set proto to string_concat(proto and ");\n") - set ok to emit(state and proto) + # Skip prototypes for functions already defined by the C runtime — a + # differing arity here would be a conflicting redeclaration. + if is_builtin_c_func(state and name) == 0: + set params to get_list(ext and 2) + set p_len to length_list(params) + set proto to "long long " + set proto to string_concat(proto and name) + set proto to string_concat(proto and "(") + set p_i to 0 + repeat while p_i < p_len: + set proto to string_concat(proto and "long long") + if p_i < p_len - 1: + set proto to string_concat(proto and ", ") + set p_i to p_i + 1 + set proto to string_concat(proto and ");\n") + set ok to emit(state and proto) set idx to idx + 1 set ok to emit(state and "\n") @@ -2230,13 +2256,18 @@ define generate_c with program and is_test_mode: repeat while idx < len: set func to get_list(funcs and idx) set name to get_list(func and 1) + # A user/imported function shadowing a C-runtime builtin is not emitted + # (the builtin wins) — so no prototype either. + if is_builtin_c_func(state and name) == 1: + set idx to idx + 1 + continue set params to get_list(func and 2) set p_len to length_list(params) - + set is_async to 0 if length_list(func) > 4: set is_async to get_list(func and 4) - + set proto to "long long " set proto to string_concat(proto and get_fn_c_name(func)) set proto to string_concat(proto and "(") @@ -2358,11 +2389,13 @@ define generate_c with program and is_test_mode: set ok to gen_function(state and method_func) set md_idx to md_idx + 1 - # Emit functions + # Emit functions (skipping any that shadow a C-runtime builtin — the + # builtin definition wins, mirroring the Rust compiler) set idx to 0 repeat while idx < len: set func to get_list(funcs and idx) - set ok to gen_function(state and func) + if is_builtin_c_func(state and get_list(func and 1)) == 0: + set ok to gen_function(state and func) set idx to idx + 1 # Emit main bootstrapper diff --git a/epc.ep b/epc.ep index a29c212..29c403a 100644 --- a/epc.ep +++ b/epc.ep @@ -245,11 +245,18 @@ define main: set pf_str to string_concat(pf and "") set pf_len_str to string_length(pf_str) - # Check for sql.ep + # Check for sql.ep — the runtime's ep_sqlite3_* wrappers are compiled + # behind #ifdef EP_HAS_SQLITE, so the define is required as well. if pf_len_str > 5: set ext_sql to substring(pf_str and pf_len_str - 6 and 6) if "sql.ep" equals ext_sql: - set compile_cmd to string_concat(compile_cmd and " -lsqlite3") + set compile_cmd to string_concat(compile_cmd and " -DEP_HAS_SQLITE -lsqlite3") + + # Check for crypto.ep (OpenSSL, matching the Rust driver's flags) + if pf_len_str > 8: + set ext_crypto to substring(pf_str and pf_len_str - 9 and 9) + if "crypto.ep" equals ext_crypto: + set compile_cmd to string_concat(compile_cmd and " -L/opt/homebrew/opt/openssl/lib -lcrypto") # Check for gui.ep if pf_len_str > 5: From 5cefd786dab90fad7df34f3e7149ba7514adffeb Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:43:58 +0100 Subject: [PATCH 08/51] feat(self-host): closures + higher-order functions (parity 26->31/54) Replaces the NODE_CLOSURE stub ('0 /* closure not yet implemented */') with the Rust compiler's EpClosure ABI: - Capture analysis via new collect_idents_expr/collect_idents_stmts walkers: identifiers read in the body that are outer variables (excluding params, known functions, ret_val), deduplicated. - Lifted function long long _ep_closure_N(long long _ep_env, params...) with captures unpacked from ((long long*)_ep_env)[i]; generated via buffer swapping on the emit list and spliced into a /* EP_CLOSURE_BODIES */ placeholder line after the prototypes (state slot 11). - Creation site mallocs {magic, fn_ptr, env[]} and packs capture values. - Indirect calls through variables use magic-number dispatch (EpClosure vs raw function pointer), so raw fn ptrs and closures share call sites. - Bare function names as values now emit (long long)name (HOF arguments). Gates: fixpoint byte-identical; parity 26->31/54 (test_closures, closure_capture/locals/indirect/foreach, hof_named now pass); run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 336 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 5 deletions(-) diff --git a/ep_codegen.ep b/ep_codegen.ep index bed693b..18da477 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -12,6 +12,133 @@ define map_get with keys and values and key: set idx to idx + 1 return 0 +define map_contains_key with keys and key: + set key_str to string_concat(key and "") + set len to length_list(keys) + set idx to 0 + repeat while idx < len: + if key_str equals get_list(keys and idx): + return 1 + set idx to idx + 1 + return 0 + +# ---- Identifier collection (closure capture analysis) ---- +# Appends every identifier name read inside an expression / statement list to +# out_names. Over-approximates (set targets, callee names, loop vars are +# included) — the closure codegen filters against the outer scope's variables, +# so spurious names are harmless. +define collect_idents_expr with expr and out_names: + if expr == 0: + return 0 + set t to get_list(expr and 0) + if t == 3: # NODE_IDENT + set ok to append_list(out_names and get_list(expr and 1)) + return 0 + if t == 4 || t == 5 || t == 14: # BINARY / COMP / LOGICAL + set okl to collect_idents_expr(get_list(expr and 1) and out_names) + set okr to collect_idents_expr(get_list(expr and 3) and out_names) + return 0 + if t == 6: # NODE_CALL — callee may itself be a captured closure variable + set ok to append_list(out_names and get_list(expr and 1)) + set args to create_list() + set args to get_list(expr and 2) + set a_len to length_list(args) + set a_i to 0 + repeat while a_i < a_len: + set oka to collect_idents_expr(get_list(args and a_i) and out_names) + set a_i to a_i + 1 + return 0 + if t == 18 || t == 20 || t == 21 || t == 32 || t == 33: # RECEIVE/BORROW/AWAIT/NOT/TRY + return collect_idents_expr(get_list(expr and 1) and out_names) + if t == 22: # FIELD_ACCESS + return collect_idents_expr(get_list(expr and 1) and out_names) + if t == 24: # STRUCT_CREATE — fields are [name, expr] pairs + set fields to get_list(expr and 2) + set f_len to length_list(fields) + set f_i to 0 + repeat while f_i < f_len: + set fpair to get_list(fields and f_i) + set okf to collect_idents_expr(get_list(fpair and 1) and out_names) + set f_i to f_i + 1 + return 0 + if t == 25: # METHOD_CALL + set okm to collect_idents_expr(get_list(expr and 1) and out_names) + set margs to get_list(expr and 3) + set m_len to length_list(margs) + set m_i to 0 + repeat while m_i < m_len: + set okma to collect_idents_expr(get_list(margs and m_i) and out_names) + set m_i to m_i + 1 + return 0 + if t == 26: # ENUM_CREATE + set eargs to get_list(expr and 2) + set e_len to length_list(eargs) + set e_i to 0 + repeat while e_i < e_len: + set oke to collect_idents_expr(get_list(eargs and e_i) and out_names) + set e_i to e_i + 1 + return 0 + if t == 34: # nested closure — walk its body; filtering handles shadowing + return collect_idents_stmts(get_list(expr and 2) and out_names) + if t == 35: # LIST_LIT + set elems to get_list(expr and 1) + set l_len to length_list(elems) + set l_i to 0 + repeat while l_i < l_len: + set okl2 to collect_idents_expr(get_list(elems and l_i) and out_names) + set l_i to l_i + 1 + return 0 + return 0 + +define collect_idents_stmts with stmts and out_names: + set len to length_list(stmts) + set idx to 0 + repeat while idx < len: + set stmt to get_list(stmts and idx) + set t to get_list(stmt and 0) + if t == 7: # SET — target counts as a read (set x to x + 1 captures x) + set ok7 to append_list(out_names and get_list(stmt and 1)) + set ok7b to collect_idents_expr(get_list(stmt and 2) and out_names) + if t == 8 || t == 9 || t == 36: # RETURN / DISPLAY / EXPR_STMT + set ok8 to collect_idents_expr(get_list(stmt and 1) and out_names) + if t == 10: # IF + set okc to collect_idents_expr(get_list(stmt and 1) and out_names) + set okt to collect_idents_stmts(get_list(stmt and 2) and out_names) + set else_b to get_list(stmt and 3) + if else_b != 0: + set oke2 to collect_idents_stmts(else_b and out_names) + if t == 11: # REPEAT_WHILE + set okw to collect_idents_expr(get_list(stmt and 1) and out_names) + set okwb to collect_idents_stmts(get_list(stmt and 2) and out_names) + if t == 15: # SPAWN + set oks to append_list(out_names and get_list(stmt and 1)) + set sargs to get_list(stmt and 2) + set s_len to length_list(sargs) + set s_i to 0 + repeat while s_i < s_len: + set oksa to collect_idents_expr(get_list(sargs and s_i) and out_names) + set s_i to s_i + 1 + if t == 16: # SEND + set okn1 to collect_idents_expr(get_list(stmt and 1) and out_names) + set okn2 to collect_idents_expr(get_list(stmt and 2) and out_names) + if t == 23: # FIELD_SET + set okf1 to collect_idents_expr(get_list(stmt and 1) and out_names) + set okf2 to collect_idents_expr(get_list(stmt and 3) and out_names) + if t == 27: # MATCH + set okm1 to collect_idents_expr(get_list(stmt and 1) and out_names) + set arms to get_list(stmt and 2) + set ar_len to length_list(arms) + set ar_i to 0 + repeat while ar_i < ar_len: + set arm to get_list(arms and ar_i) + set okab to collect_idents_stmts(get_list(arm and 2) and out_names) + set ar_i to ar_i + 1 + if t == 28: # FOR_EACH + set okfe1 to collect_idents_expr(get_list(stmt and 2) and out_names) + set okfe2 to collect_idents_stmts(get_list(stmt and 3) and out_names) + set idx to idx + 1 + return 0 + define map_put with keys and values and key and val: set key_str to string_concat(key and "") set len to length_list(keys) @@ -150,6 +277,7 @@ define create_codegen_state: set ok to append_list(state and (create_list() + 0)) # current_borrowed_keys (8) set ok to append_list(state and (create_list() + 0)) # current_borrowed_values (9) set ok to append_list(state and 0) # builtin_count (10): entries in func_keys[0..n) that are C-runtime builtins + set ok to append_list(state and (create_list() + 0)) # closure_bodies (11): lifted closure C functions, spliced at the marker return state # A user/imported .ep function whose name matches a C-runtime builtin must NOT @@ -1236,6 +1364,14 @@ define gen_expr with state and expr and var_keys and var_values: if type == 3: # NODE_IDENT set name to get_list(expr and 1) + # A bare function name used as a value (HOF argument, `set f to double`) + # becomes its function pointer. Local variables shadow function names. + if map_contains_key(var_keys and name) == 0: + set fk to get_list(state and 3) + if map_contains_key(fk and name) == 1: + set fres to "(long long)" + set fres to string_concat(fres and name) + return fres return name if type == 4: # NODE_BINARY @@ -1396,7 +1532,42 @@ define gen_expr with state and expr and var_keys and var_values: set ok to append_list(args_str_list and ", ") set idx to idx + 1 set args_joined to join_strings(args_str_list) - + + # Indirect call through a variable (not a known function): the value + # may be an EpClosure or a raw function pointer — dispatch on the + # magic number, mirroring the Rust compiler. + set func_keys to get_list(state and 3) + if map_contains_key(func_keys and name) == 0: + if map_contains_key(var_keys and name) == 1: + set cl_sig to "long long(*)(long long" + set rw_sig to "long long(*)(" + set sg_i to 0 + repeat while sg_i < args_len: + set cl_sig to string_concat(cl_sig and ", long long") + if sg_i > 0: + set rw_sig to string_concat(rw_sig and ", long long") + else: + set rw_sig to string_concat(rw_sig and "long long") + set sg_i to sg_i + 1 + if args_len == 0: + set rw_sig to string_concat(rw_sig and "void") + set cl_sig to string_concat(cl_sig and ")") + set rw_sig to string_concat(rw_sig and ")") + set res to "({ long long _fv = " + set res to string_concat(res and name) + set res to string_concat(res and "; EpClosure* _cl = (EpClosure*)_fv; (_fv != 0 && _cl->magic == EP_CLOSURE_MAGIC) ? ((") + set res to string_concat(res and cl_sig) + set res to string_concat(res and ")_cl->fn_ptr)((long long)_cl->env") + if args_len > 0: + set res to string_concat(res and ", ") + set res to string_concat(res and args_joined) + set res to string_concat(res and ") : ((") + set res to string_concat(res and rw_sig) + set res to string_concat(res and ")_fv)(") + set res to string_concat(res and args_joined) + set res to string_concat(res and "); })") + return res + set call_str to name set call_str to string_concat(call_str and "(") set call_str to string_concat(call_str and args_joined) @@ -1526,12 +1697,154 @@ define gen_expr with state and expr and var_keys and var_values: return inner_str if type == 34: # NODE_CLOSURE - # Closures: for now, emit as named static functions (simplified) + # Mirrors the Rust compiler's EpClosure ABI: + # long long _ep_closure_N(long long _ep_env, ) + # captures unpacked from ((long long*)_ep_env)[i]; the creation site + # mallocs {magic, fn_ptr, env[]} and packs current capture values. set params to get_list(expr and 1) set body to get_list(expr and 2) - set label to get_new_label(state and "closure") - # Emit closure as inline block returning function pointer cast - return "0 /* closure not yet implemented */" + set cidx to get_codegen_spawn_index(state) + set dummy to set_codegen_spawn_index(state and cidx + 1) + set cname to string_concat("_ep_closure_" and cg_int_to_str(cidx)) + + # ---- capture analysis: identifiers read in the body that are outer + # variables (not params, not functions, not ret_val) ---- + set raw_names to create_list() + set okr to collect_idents_stmts(body and raw_names) + set func_keys to get_list(state and 3) + set p_len to length_list(params) + set captured to create_list() + set rn_len to length_list(raw_names) + set rn_i to 0 + repeat while rn_i < rn_len: + set nm to get_list(raw_names and rn_i) + set skip to 0 + if nm equals "ret_val": + set skip to 1 + if skip == 0: + if map_contains_key(captured and nm) == 1: + set skip to 1 + if skip == 0: + set pp_i to 0 + repeat while pp_i < p_len: + set p_node to get_list(params and pp_i) + if nm equals get_list(p_node and 0): + set skip to 1 + set pp_i to pp_i + 1 + if skip == 0: + if map_contains_key(var_keys and nm) == 0: + set skip to 1 + if skip == 0: + if map_contains_key(func_keys and nm) == 1: + set skip to 1 + if skip == 0: + set okc to append_list(captured and nm) + set rn_i to rn_i + 1 + set n_caps to length_list(captured) + + # ---- generate the lifted function into a swap buffer ---- + set saved_lines to get_list(state and 0) + set fresh to create_list() + 0 + set dummy2 to set_list(state and 0 and fresh) + + set hdr to "long long " + set hdr to string_concat(hdr and cname) + set hdr to string_concat(hdr and "(long long _ep_env") + set hp_i to 0 + repeat while hp_i < p_len: + set p_node to get_list(params and hp_i) + set hdr to string_concat(hdr and ", long long ") + set hdr to string_concat(hdr and get_list(p_node and 0)) + set hp_i to hp_i + 1 + set hdr to string_concat(hdr and ") {\n long long ret_val = 0;\n") + set okh to emit(state and hdr) + set cp_i to 0 + repeat while cp_i < n_caps: + set unp to " long long " + set unp to string_concat(unp and get_list(captured and cp_i)) + set unp to string_concat(unp and " = ((long long*)_ep_env)[") + set unp to string_concat(unp and cg_int_to_str(cp_i)) + set unp to string_concat(unp and "];\n") + set oku to emit(state and unp) + set cp_i to cp_i + 1 + + # closure-body scope: outer vars + params + body locals + set c_keys to create_list() + set c_values to create_list() + set ov_len to length_list(var_keys) + set ov_i to 0 + repeat while ov_i < ov_len: + set okov to map_put(c_keys and c_values and get_list(var_keys and ov_i) and get_list(var_values and ov_i)) + set ov_i to ov_i + 1 + set pv_i to 0 + repeat while pv_i < p_len: + set p_node to get_list(params and pv_i) + set okpv to map_put(c_keys and c_values and get_list(p_node and 0) and 1) + set pv_i to pv_i + 1 + set b_keys to create_list() + set b_values to create_list() + set okbv to collect_var_types(state and body and b_keys and b_values) + set bv_len to length_list(b_keys) + set bv_i to 0 + repeat while bv_i < bv_len: + set bname to get_list(b_keys and bv_i) + set is_p to 0 + set bp_i to 0 + repeat while bp_i < p_len: + set p_node to get_list(params and bp_i) + if bname equals get_list(p_node and 0): + set is_p to 1 + set bp_i to bp_i + 1 + if is_p == 0: + if map_contains_key(captured and bname) == 0: + if bname equals "ret_val": + set is_p to 1 + else: + set dec to " long long " + set dec to string_concat(dec and bname) + set dec to string_concat(dec and " = 0;\n") + set okd to emit(state and dec) + set okbm to map_put(c_keys and c_values and bname and get_list(b_values and bv_i)) + set bv_i to bv_i + 1 + + set bs_len to length_list(body) + set bs_i to 0 + repeat while bs_i < bs_len: + set okst to gen_statement(state and get_list(body and bs_i) and c_keys and c_values) + set bs_i to bs_i + 1 + set okft to emit(state and "L_cleanup:\n return ret_val;\n}\n\n") + + set closure_text to join_strings(fresh) + set dummy3 to set_list(state and 0 and saved_lines) + set cbodies to get_list(state and 11) + set okcb to append_list(cbodies and closure_text) + + # ---- creation expression ---- + set res to "({ EpClosure* _cl_" + set res to string_concat(res and cg_int_to_str(cidx)) + set res to string_concat(res and " = (EpClosure*)malloc(sizeof(EpClosure) + ") + set res to string_concat(res and cg_int_to_str(n_caps)) + set res to string_concat(res and " * sizeof(long long)); _cl_") + set res to string_concat(res and cg_int_to_str(cidx)) + set res to string_concat(res and "->magic = EP_CLOSURE_MAGIC; _cl_") + set res to string_concat(res and cg_int_to_str(cidx)) + set res to string_concat(res and "->fn_ptr = (long long)") + set res to string_concat(res and cname) + set res to string_concat(res and ";") + set ce_i to 0 + repeat while ce_i < n_caps: + set res to string_concat(res and " _cl_") + set res to string_concat(res and cg_int_to_str(cidx)) + set res to string_concat(res and "->env[") + set res to string_concat(res and cg_int_to_str(ce_i)) + set res to string_concat(res and "] = ") + set res to string_concat(res and get_list(captured and ce_i)) + set res to string_concat(res and ";") + set ce_i to ce_i + 1 + set res to string_concat(res and " (long long)_cl_") + set res to string_concat(res and cg_int_to_str(cidx)) + set res to string_concat(res and "; })") + return res if type == 35: # NODE_LIST_LIT set elements to get_list(expr and 1) @@ -2358,6 +2671,13 @@ define generate_c with program and is_test_mode: set idx to idx + 1 set ok to emit(state and "\n") + # Closure bodies are lifted to top level during function generation; they + # must appear after the runtime + prototypes but before their users. Emit a + # placeholder line here and splice the accumulated bodies into it at the end. + set out_lines to get_list(state and 0) + set closure_slot to length_list(out_lines) + set ok to emit(state and "\n/* EP_CLOSURE_BODIES */\n") + # Reset spawn index set dummy to set_codegen_spawn_index(state and 0) @@ -2404,7 +2724,13 @@ define generate_c with program and is_test_mode: else: set ok to emit(state and get_c_main_source()) + # Splice lifted closure bodies into the placeholder slot. set lines to get_list(state and 0) + set cbodies to get_list(state and 11) + if length_list(cbodies) > 0: + set marker_line to get_list(lines and closure_slot) + set spliced to string_concat(marker_line and join_strings(cbodies)) + set oksp to set_list(lines and closure_slot and spliced) set c_code to join_strings(lines) return c_code From cdb69984759c4e36636da1f1a53c7a0e0621d75d Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:50:15 +0100 Subject: [PATCH 09/51] fix(self-host): 'equals' on unknown-typed operands compiled to pointer compare (parity 31->32/54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit is_builtin_c_func and the closure capture filters compared strings with 'x equals y' where x had unknown type — the codegen only emits strcmp when the LEFT operand is known to be a string, so these compiled to pointer comparisons that were always false (the extern-prototype guard silently never fired, leaving conflicting string_length/... redeclarations). Coerce via string_concat(x and "") first — the same idiom map_get/map_put use. Gates: fixpoint byte-identical; parity 31->32; run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ep_codegen.ep b/ep_codegen.ep index 18da477..747fd39 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -284,11 +284,15 @@ define create_codegen_state: # be emitted — the C builtin wins (mirrors the Rust compiler's builtin_c_funcs # skip). Builtins occupy the first builtin_count slots of func_keys. define is_builtin_c_func with state and name: + # Coerce to a known-string type so `equals` compiles to strcmp, not a + # pointer comparison (the codegen only string-compares when the LEFT + # operand's type is known to be a string — same idiom as map_get). + set name_str to string_concat(name and "") set keys to get_list(state and 3) set n to get_list(state and 10) set idx to 0 repeat while idx < n: - if name equals get_list(keys and idx): + if name_str equals get_list(keys and idx): return 1 set idx to idx + 1 return 0 @@ -1717,7 +1721,8 @@ define gen_expr with state and expr and var_keys and var_values: set rn_len to length_list(raw_names) set rn_i to 0 repeat while rn_i < rn_len: - set nm to get_list(raw_names and rn_i) + # Coerce so `equals` is a string comparison (see is_builtin_c_func). + set nm to string_concat(get_list(raw_names and rn_i) and "") set skip to 0 if nm equals "ret_val": set skip to 1 @@ -1787,7 +1792,8 @@ define gen_expr with state and expr and var_keys and var_values: set bv_len to length_list(b_keys) set bv_i to 0 repeat while bv_i < bv_len: - set bname to get_list(b_keys and bv_i) + # Coerce so `equals` is a string comparison (see is_builtin_c_func). + set bname to string_concat(get_list(b_keys and bv_i) and "") set is_p to 0 set bp_i to 0 repeat while bp_i < p_len: From abe81a355ba8474aeca585566114b64e1c894ac1 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Wed, 1 Jul 2026 23:53:53 +0100 Subject: [PATCH 10/51] fix(self-host): purge non-runtime names from builtin registry; hardened fixpoint gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit made is_builtin_c_func actually work, which exposed three registry entries that are NOT C-runtime symbols (string_concat is a compiler- internal .ep helper; float_to_string / ep_time_ms don't exist in the shared runtime). Their .ep definitions were suppressed by the new skip, breaking self-compilation (undeclared string_concat) — and the ad-hoc fixpoint check compared stale binaries, masking the failure. - Remove the three bogus registry entries (verified every remaining entry against runtime/*.c symbol definitions). - tests/run_fixpoint.sh: proper gate that checks every stage's exit code and runs a basic-math smoke test on the fixpoint binary. Gates: FIXPOINT OK (3-stage, rc-checked); parity 32/54; run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 3 --- tests/run_fixpoint.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100755 tests/run_fixpoint.sh diff --git a/ep_codegen.ep b/ep_codegen.ep index 747fd39..56fe975 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -408,7 +408,6 @@ define analyze_return_types with state and program: set ok to map_put(keys and values and "ep_sleep_ms" and 1) # String builtins from the shared runtime (TYPE_DYNSTR = 3 unless noted) set ok to map_put(keys and values and "concat" and 3) - set ok to map_put(keys and values and "string_concat" and 3) set ok to map_put(keys and values and "ep_auto_to_string" and 3) set ok to map_put(keys and values and "int_to_string" and 3) set ok to map_put(keys and values and "ep_int_to_str" and 3) @@ -424,7 +423,6 @@ define analyze_return_types with state and program: set ok to map_put(keys and values and "char_from_code" and 3) set ok to map_put(keys and values and "ptr_to_str" and 3) set ok to map_put(keys and values and "str_to_ptr" and 1) - set ok to map_put(keys and values and "float_to_string" and 3) set ok to map_put(keys and values and "ep_sb_to_string" and 3) set ok to map_put(keys and values and "ep_sb_create" and 1) set ok to map_put(keys and values and "ep_sb_append" and 1) @@ -432,7 +430,6 @@ define analyze_return_types with state and program: # Math / time / system (TYPE_INT = 1) set ok to map_put(keys and values and "ep_random_int" and 1) set ok to map_put(keys and values and "ep_abs" and 1) - set ok to map_put(keys and values and "ep_time_ms" and 1) set ok to map_put(keys and values and "ep_time_now_ms" and 1) set ok to map_put(keys and values and "ep_time_now_sec" and 1) set ok to map_put(keys and values and "ep_time_day" and 1) diff --git a/tests/run_fixpoint.sh b/tests/run_fixpoint.sh new file mode 100755 index 0000000..59ce9c6 --- /dev/null +++ b/tests/run_fixpoint.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# run_fixpoint.sh — the self-hosting fixpoint gate. +# +# gen1 = epc built by the Rust compiler +# gen2 = epc built by gen1 (must succeed) +# gen3 = epc built by gen2 (must succeed) +# gen2 and gen3 must be byte-identical. +# +# Every stage's exit code is checked: a stale binary passing `cmp` must never +# masquerade as a fixpoint. +set -uo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$SCRIPT_DIR" +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT + +./target/release/ernos epc.ep >"$TMP/g1.log" 2>&1 || { echo "FIXPOINT FAIL: Rust compiler cannot build epc.ep"; tail -5 "$TMP/g1.log"; exit 1; } +cp epc "$TMP/gen1" +"$TMP/gen1" epc.ep >"$TMP/g2.log" 2>&1 || { echo "FIXPOINT FAIL: gen1 epc cannot compile epc.ep"; grep -iE "error" "$TMP/g2.log" | head -5; exit 1; } +cp epc "$TMP/gen2" +"$TMP/gen2" epc.ep >"$TMP/g3.log" 2>&1 || { echo "FIXPOINT FAIL: gen2 epc cannot compile epc.ep"; grep -iE "error" "$TMP/g3.log" | head -5; exit 1; } +cp epc "$TMP/gen3" +cmp -s "$TMP/gen2" "$TMP/gen3" || { echo "FIXPOINT FAIL: gen2 and gen3 binaries differ"; exit 1; } +./epc tests/test_basic_math.ep >/dev/null 2>&1 && ./tests/test_basic_math >/dev/null 2>&1 || ./test_basic_math >/dev/null 2>&1 || { echo "FIXPOINT FAIL: fixpoint epc cannot compile+run test_basic_math"; exit 1; } +rm -f test_basic_math test_basic_math_compiled.c tests/test_basic_math 2>/dev/null +echo "FIXPOINT OK: 3-stage byte-identical, basic-math smoke passes" From e85fe5f86761024e8d549a88af4584dfece6ebab Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 00:00:54 +0100 Subject: [PATCH 11/51] feat(self-host): English keyword aliases + keyword-as-identifier leniency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ep_lexer.ep: describe/let/be/show/print/loop/every/returns/stop/skip/times keyword aliases and the 'give back' multi-phrase (mirrors src/lexer.rs). - ep_parser.ep: keyword tokens double as variable names in expression position (mirrors the Rust parser's expect_identifier leniency) — required so sources using loop/skip/show/etc. as variables keep compiling now that those words lex as keywords. Set targets already used the token value. test_english_aliases still has a tail of phrase-level aliases (comparison phrases, tiered forms) — tracked, not yet passing. Gates: FIXPOINT OK; parity 32/54 held; run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_lexer.ep | 35 ++++++++++++++++++++++++++++++++++- ep_parser.ep | 8 +++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/ep_lexer.ep b/ep_lexer.ep index a5bb12f..9915bb6 100644 --- a/ep_lexer.ep +++ b/ep_lexer.ep @@ -445,6 +445,16 @@ define tokenize_source with source: set pos to next_p set is_multi_phrase to 1 + if is_multi_phrase == 0: + if "give" equals id_str: + set next_p to match_next_word(source and pos and "back") + if next_p > 0: + set tok_type to 6 # TOK_RETURN ("give back" alias) + set id_str to "return" + set current_col to current_col + (next_p - pos) + set pos to next_p + set is_multi_phrase to 1 + if is_multi_phrase == 0: if "or" equals id_str: set next_p to match_next_word(source and pos and "else") @@ -553,7 +563,30 @@ define tokenize_source with source: set tok_type to 66 if "modulo" equals id_str: set tok_type to 41 - + # English keyword aliases (mirror src/lexer.rs) + if "describe" equals id_str: + set tok_type to 1 + if "let" equals id_str: + set tok_type to 2 + if "be" equals id_str: + set tok_type to 3 + if "show" equals id_str: + set tok_type to 7 + if "print" equals id_str: + set tok_type to 7 + if "loop" equals id_str: + set tok_type to 8 + if "every" equals id_str: + set tok_type to 54 + if "returns" equals id_str: + set tok_type to 43 + if "stop" equals id_str: + set tok_type to 61 + if "skip" equals id_str: + set tok_type to 62 + if "times" equals id_str: + set tok_type to 14 + set tok to create_token(tok_type and id_str and current_line and current_col - id_len) + 0 set ok to append_list(tokens and tok) else: diff --git a/ep_parser.ep b/ep_parser.ep index 28537e8..0bf3238 100644 --- a/ep_parser.ep +++ b/ep_parser.ep @@ -1244,7 +1244,13 @@ define parse_prefix with state: if t == 69: # [ (list literal) return parse_list_literal(state) + 0 - + + # Keyword tokens that double as variable names in expression position + # (mirrors the Rust parser's expect_identifier leniency — e.g. variables + # named loop, skip, show, check, field…). The token value holds the word. + if t == 1 || t == 7 || t == 8 || t == 43 || t == 44 || t == 45 || t == 47 || t == 48 || t == 49 || t == 54 || t == 57 || t == 58 || t == 60 || t == 61 || t == 62: + set t to 27 + if t == 27: # TOK_IDENTIFIER set name to get_token_value(tok) # Check for function call: name(args) From 1cad11e1544e79ffeb4196b137ae3e230fd9dd4e Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 00:04:07 +0100 Subject: [PATCH 12/51] feat(self-host): import "x" as alias (parity 32->33/54) - ep_parser.ep: optional 'as ' after the import path; import entries become [path, alias] pairs. - epc.ep: aliased modules are parsed into fresh lists, then merged with BOTH original names (intra-module calls) and alias_-prefixed duplicates (importer- facing), mirroring the Rust driver's flattening. Gates: FIXPOINT OK; parity 32->33/54 (test_import_alias); run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_parser.ep | 13 +++++++++++- epc.ep | 57 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/ep_parser.ep b/ep_parser.ep index 0bf3238..d1ea00e 100644 --- a/ep_parser.ep +++ b/ep_parser.ep @@ -522,7 +522,18 @@ define parse_program with state: set path_type to get_token_type(tok_path) if path_type == 26: # string set path_val to get_token_value(tok_path) - set ok to append_list(imports and path_val) + # Optional namespace alias: import "x" as m + set alias_val to "" + set peek_as to peek_token(state) + if get_token_type(peek_as) == 42: # as + set dummy2 to advance_token(state) + set tok_alias to advance_token(state) + set alias_val to get_token_value(tok_alias) + # Each import entry is a [path, alias] pair (alias "" if none) + set imp_pair to create_list() + 0 + set ok to append_list(imp_pair and path_val) + set ok to append_list(imp_pair and alias_val) + set ok to append_list(imports and imp_pair) else: display "Parser Error: Expected string literal after import at line:" display get_token_line(tok_path) diff --git a/epc.ep b/epc.ep index 29c403a..091db02 100644 --- a/epc.ep +++ b/epc.ep @@ -149,13 +149,60 @@ define parse_all_modules with current_file and parsed_files and all_functions an set imp_len to length_list(imports) set i_idx to 0 repeat while i_idx < imp_len: - set imp to get_list(imports and i_idx) + # Import entries are [path, alias] pairs (alias "" when no `as`). + set imp_pair to get_list(imports and i_idx) + set imp to get_list(imp_pair and 0) + set imp_alias to string_concat(get_list(imp_pair and 1) and "") set resolved_path to resolve_import_path(current_file and imp) - set status to parse_all_modules(resolved_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls) - if status != 0: - return status + if string_length(imp_alias) == 0: + set status to parse_all_modules(resolved_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls) + if status != 0: + return status + else: + # Aliased import: parse the module into fresh lists, then add BOTH + # the original names (the module's functions call each other) and + # alias_-prefixed duplicates (what the importer calls) — mirroring + # the Rust driver. + set mod_funcs to create_list() + set mod_externals to create_list() + set status to parse_all_modules(resolved_path and parsed_files and mod_funcs and mod_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls) + if status != 0: + return status + set mf_len to length_list(mod_funcs) + set mf_i to 0 + repeat while mf_i < mf_len: + set mfunc to get_list(mod_funcs and mf_i) + set ok to append_list(all_functions and mfunc) + # shallow copy with alias_-prefixed name + set acopy to create_list() + 0 + set mfl to length_list(mfunc) + set mc_i to 0 + repeat while mc_i < mfl: + set ok2 to append_list(acopy and get_list(mfunc and mc_i)) + set mc_i to mc_i + 1 + set aname to string_concat(imp_alias and "_") + set aname to string_concat(aname and get_list(mfunc and 1)) + set ok3 to set_list(acopy and 1 and aname) + set ok4 to append_list(all_functions and acopy) + set mf_i to mf_i + 1 + set me_len to length_list(mod_externals) + set me_i to 0 + repeat while me_i < me_len: + set mext to get_list(mod_externals and me_i) + set ok5 to append_list(all_externals and mext) + set ecopy to create_list() + 0 + set mel to length_list(mext) + set me_ci to 0 + repeat while me_ci < mel: + set ok6 to append_list(ecopy and get_list(mext and me_ci)) + set me_ci to me_ci + 1 + set ename to string_concat(imp_alias and "_") + set ename to string_concat(ename and get_list(mext and 1)) + set ok7 to set_list(ecopy and 1 and ename) + set ok8 to append_list(all_externals and ecopy) + set me_i to me_i + 1 set i_idx to i_idx + 1 - + return 0 define main: From 3eeec0fcd2dc2c5312d55ee35d8281fa49ef1693 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 00:07:45 +0100 Subject: [PATCH 13/51] feat(self-host): bare enum-variant identifiers construct the variant (parity 33->34/54) 'set d to North' / 'Leaf' etc. parsed as plain identifiers and emitted as undeclared C names. Variant names are now registered during the tag-define walk (state slot 12); NODE_IDENT resolves them to the zero-payload enum construction (same emission as NODE_ENUM_CREATE with no args). Locals shadow variant names. Gates: FIXPOINT OK; parity 33->34/54 (test_bst, test_recursive_enum); run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ep_codegen.ep b/ep_codegen.ep index 56fe975..f35780e 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -278,6 +278,7 @@ define create_codegen_state: set ok to append_list(state and (create_list() + 0)) # current_borrowed_values (9) set ok to append_list(state and 0) # builtin_count (10): entries in func_keys[0..n) that are C-runtime builtins set ok to append_list(state and (create_list() + 0)) # closure_bodies (11): lifted closure C functions, spliced at the marker + set ok to append_list(state and (create_list() + 0)) # enum_variant_names (12): every declared variant, for bare-variant identifiers return state # A user/imported .ep function whose name matches a C-runtime builtin must NOT @@ -1365,6 +1366,14 @@ define gen_expr with state and expr and var_keys and var_values: if type == 3: # NODE_IDENT set name to get_list(expr and 1) + # A bare enum variant (e.g. `North`) constructs the zero-payload + # variant. Local variables shadow variant names. + if map_contains_key(var_keys and name) == 0: + if map_contains_key(get_list(state and 12) and name) == 1: + set eres to "({ long long* _v = (long long*)malloc(sizeof(long long) * 1); _v[0] = EP_TAG_" + set eres to string_concat(eres and name) + set eres to string_concat(eres and "; ep_gc_register(_v, EP_OBJ_STRUCT); (long long)_v; })") + return eres # A bare function name used as a value (HOF argument, `set f to double`) # becomes its function pointer. Local variables shadow function names. if map_contains_key(var_keys and name) == 0: @@ -2522,6 +2531,7 @@ define generate_c with program and is_test_mode: set ev to get_list(evariants and ev_idx) set ev_name to get_list(ev and 0) set tslot to field_slot_index(tag_slots and ev_name) + set okvn to append_list(get_list(state and 12) and ev_name) set ev_idx to ev_idx + 1 set ed_idx to ed_idx + 1 set ts_len to length_list(tag_slots) From a6a61261786f5d3ad406900d661d341e317d6361 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 00:13:10 +0100 Subject: [PATCH 14/51] feat(self-host): boolean display (true/false) (parity 34->35/54) Bool literals and 'not' expressions now infer TYPE_BOOL (7); display emits (v) ? "true" : "false" instead of %lld, matching the Rust compiler. Comparison/logical results stay int-typed to avoid arithmetic-context regressions. Gates: FIXPOINT OK; parity 34->35/54 (test_core); run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- ep_codegen.ep | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ep_codegen.ep b/ep_codegen.ep index f35780e..f563e14 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -617,6 +617,8 @@ define infer_type with state and expr and var_keys and var_values: return 1 if type == 4 || type == 5 || type == 14: # NODE_BINARY, NODE_COMP, NODE_LOGICAL return 1 + if type == 31 || type == 32: # NODE_BOOL_LIT, NODE_UNARY_NOT -> TYPE_BOOL = 7 + return 7 if type == 6: # NODE_CALL set name to get_list(expr and 1) set func_keys to get_list(state and 3) @@ -1114,6 +1116,12 @@ define gen_statement with state and stmt and var_keys and var_values: set line to string_concat(line and ");\n") set ok to emit(state and line) else: + if t == 7: # TYPE_BOOL + set line to " printf(\"%s\\n\", (" + set line to string_concat(line and expr_str) + set line to string_concat(line and ") ? \"true\" : \"false\");\n") + set ok to emit(state and line) + else: set line to " printf(\"%lld\\n\", " set line to string_concat(line and expr_str) set line to string_concat(line and ");\n") From 1de9c36c9520e754d426c199089aba1a6b0f703c Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 00:15:58 +0100 Subject: [PATCH 15/51] =?UTF-8?q?feat(bootstrap):=20frozen=20pure-C=20boot?= =?UTF-8?q?strap=20=E2=80=94=20toolchain=20builds=20without=20Rust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delivers the 'fully self-contained' milestone. bootstrap/epc_bootstrap.c is epc compiled by epc (the fixpoint C output, 19,580 lines). From it the whole toolchain builds with only clang: bash bootstrap/build.sh # clang epc_bootstrap.c -> epc, then epc rebuilds epc.ep bootstrap/verify.sh proves self-containment: clang-only build -> 3-stage byte-identical fixpoint from the frozen C (Rust never invoked) -> freshness check (committed artifact must match what epc.ep produces today) -> epc parity suite. All green. The Rust compiler is now needed only for LSP + cross-language transpilers. Co-Authored-By: Claude Fable 5 --- .gitignore | 3 + bootstrap/build.sh | 18 + bootstrap/epc_bootstrap.c | 19580 ++++++++++++++++++++++++++++++++++++ bootstrap/verify.sh | 43 + 4 files changed, 19644 insertions(+) create mode 100755 bootstrap/build.sh create mode 100644 bootstrap/epc_bootstrap.c create mode 100755 bootstrap/verify.sh diff --git a/.gitignore b/.gitignore index a6e8cf4..06dadda 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,6 @@ tests/* !tests/*.expected !tests/*.md !tests/*.sh + +# Bootstrap build output (keep the frozen C, not the binary) +/epc-bootstrapped diff --git a/bootstrap/build.sh b/bootstrap/build.sh new file mode 100755 index 0000000..3e549d6 --- /dev/null +++ b/bootstrap/build.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# build.sh — build the ErnosPlain self-hosted compiler from the frozen C +# bootstrap, using ONLY a C compiler. No Rust, no cargo. +# +# bootstrap/epc_bootstrap.c is epc compiled by epc (the fixpoint output). +# Compiling it yields a working ./epc, which can then compile epc.ep (and any +# .ep program) directly. +# +# Usage: bash bootstrap/build.sh -> produces ./epc in the repo root +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" +CC="${CC:-clang}" +echo "[bootstrap] compiling epc from frozen C with $CC ..." +"$CC" -O2 bootstrap/epc_bootstrap.c -o epc-bootstrapped -lpthread +echo "[bootstrap] rebuilding epc.ep with the bootstrapped compiler ..." +./epc-bootstrapped epc.ep +echo "[bootstrap] done — ./epc is ready (Rust was not used)." diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c new file mode 100644 index 0000000..d7328a5 --- /dev/null +++ b/bootstrap/epc_bootstrap.c @@ -0,0 +1,19580 @@ +#include +#include +#include +#include +#ifdef __wasm__ +#define _SETJMP_H +typedef int jmp_buf[1]; +#define setjmp(buf) (0) +#define longjmp(buf, val) abort() + +// Mock pthreads for single-threaded WASM +typedef struct { int lock_state; } pthread_mutex_t; +typedef struct { int cond_state; } pthread_cond_t; +typedef struct { int rw_state; } pthread_rwlock_t; +typedef int pthread_t; +typedef int pthread_attr_t; +#define PTHREAD_MUTEX_INITIALIZER {0} +#define PTHREAD_COND_INITIALIZER {0} +#define PTHREAD_RWLOCK_INITIALIZER {0} +#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0) +#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0) +#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0) +#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1) +#define pthread_mutex_destroy(m) ((void)(m), 0) +#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0) +#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0) +#define pthread_cond_signal(c) ((void)(c), 0) +#define pthread_cond_broadcast(c) ((void)(c), 0) +#define pthread_cond_destroy(c) ((void)(c), 0) +#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0) +#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0) +#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0) +#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0) +#define pthread_rwlock_destroy(r) ((void)(r), 0) +#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0) +#define pthread_join(t, r) ((void)(t), (void)(r), 0) +#define pthread_detach(t) ((void)(t), 0) +#else +#include +#endif +#include +#include +#ifndef _WIN32 +#include +#endif +#if defined(__APPLE__) +#include +#endif +#if defined(__linux__) +#include +#endif +#include + +/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on + Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a + /dev/urandom read elsewhere. Only if all of those are unavailable does it + fall back to rand() — never on a supported platform. */ +static void ep_secure_random_bytes(unsigned char* buf, size_t n) { +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + arc4random_buf(buf, n); +#else + size_t got = 0; + #if defined(__linux__) + while (got < n) { + ssize_t r = getrandom(buf + got, n - got, 0); + if (r <= 0) break; + got += (size_t)r; + } + #endif + if (got < n) { + FILE* f = fopen("/dev/urandom", "rb"); + if (f) { + got += fread(buf + got, 1, n - got, f); + fclose(f); + } + } + while (got < n) { + buf[got++] = (unsigned char)(rand() & 0xFF); + } +#endif +} + +/* Try/catch infrastructure */ +static jmp_buf ep_try_buf; +static volatile int ep_try_active = 0; + +static void ep_signal_handler(int sig) { + if (ep_try_active) { + ep_try_active = 0; + longjmp(ep_try_buf, sig); + } + /* Outside try: print error and exit */ + const char* name = sig == SIGSEGV ? "segmentation fault (null pointer or invalid memory access)" + : sig == SIGFPE ? "arithmetic error (division by zero)" + : sig == SIGABRT ? "aborted" + : "unknown signal"; + fprintf(stderr, "\nRuntime Error: %s (signal %d)\n", name, sig); + + /* Write to daemon/general log file if environment variable is set */ + const char* daemon_log = getenv("ERNOS_DAEMON_LOG"); + if (!daemon_log || daemon_log[0] == '\0') { + daemon_log = getenv("ERNOS_LOG_FILE"); + } + if (daemon_log && daemon_log[0] != '\0') { + FILE* f = fopen(daemon_log, "ab"); + if (f) { + time_t rawtime; + time(&rawtime); + struct tm * timeinfo = localtime(&rawtime); + char time_buf[80]; + if (timeinfo) { + strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", timeinfo); + } else { + snprintf(time_buf, sizeof(time_buf), "%lld", (long long)rawtime); + } + fprintf(f, "[%s] FATAL: Runtime Error: %s (signal %d)\n", time_buf, name, sig); + fclose(f); + } + } + + _exit(128 + sig); +} + +#ifdef _MSC_VER +static void ep_install_signal_handlers(void); +#pragma section(".CRT$XCU", read) +__declspec(allocate(".CRT$XCU")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers; +static void ep_install_signal_handlers(void) { +#else +__attribute__((constructor)) +static void ep_install_signal_handlers(void) { +#endif + signal(SIGFPE, ep_signal_handler); + signal(SIGSEGV, ep_signal_handler); + signal(SIGABRT, ep_signal_handler); +#ifdef _WIN32 + { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); } +#endif +} + +#if defined(__wasm__) + typedef int ep_thread_t; + typedef int ep_mutex_t; + typedef int ep_cond_t; + #define ep_mutex_init(m) (void)(0) + #define ep_mutex_lock(m) (void)(0) + #define ep_mutex_unlock(m) (void)(0) + #define ep_cond_init(c) (void)(0) + #define ep_cond_wait(c, m) (void)(0) + #define ep_cond_signal(c) (void)(0) +#elif defined(_WIN32) + #include + #include + #include + #pragma comment(lib, "ws2_32.lib") + typedef HANDLE ep_thread_t; + typedef CRITICAL_SECTION ep_mutex_t; + typedef CONDITION_VARIABLE ep_cond_t; + #define ep_mutex_init(m) InitializeCriticalSection(m) + #define ep_mutex_lock(m) EnterCriticalSection(m) + #define ep_mutex_unlock(m) LeaveCriticalSection(m) + #define ep_cond_init(c) InitializeConditionVariable(c) + #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE) + #define ep_cond_signal(c) WakeConditionVariable(c) +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + typedef pthread_t ep_thread_t; + typedef pthread_mutex_t ep_mutex_t; + typedef pthread_cond_t ep_cond_t; + #define ep_mutex_init(m) pthread_mutex_init(m, NULL) + #define ep_mutex_lock(m) pthread_mutex_lock(m) + #define ep_mutex_unlock(m) pthread_mutex_unlock(m) + #define ep_cond_init(c) pthread_cond_init(c, NULL) + #define ep_cond_wait(c, m) pthread_cond_wait(c, m) + #define ep_cond_signal(c) pthread_cond_signal(c) +#endif + +/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */ + +#include +#if !defined(__wasm__) && !defined(_WIN32) +#include +#endif + +typedef enum { + EP_OBJ_LIST, + EP_OBJ_STRING, + EP_OBJ_STRUCT, + EP_OBJ_CLOSURE, + EP_OBJ_MAP +} EpObjKind; + +typedef struct EpGCObject { + EpObjKind kind; + int marked; + void* ptr; /* actual allocation pointer */ + long long size; /* payload size for structs */ + long long num_fields; /* number of fields for structs (each is long long) */ + int generation; /* 0 = Nursery/young, 1 = Old */ + struct EpGCObject* next; /* intrusive linked list */ +} EpGCObject; + +long long ep_time_now_ms(void); +long long ep_sleep_ms(long long ms); + +typedef struct EpTask EpTask; +typedef struct { + long long chan; + int completed; + long long value; + EpTask* waiting_task; +} EpFuture; + +static long long ep_await_future(EpFuture* fut); + +struct EpTask { + long long (*step)(void*); /* pointer to step function */ + void* args; /* pointer to step state arguments */ + long long args_size_bytes; /* size of args struct for GC tracing */ + EpTask* next; /* run-queue link pointer */ + EpFuture* fut; /* future associated with this task */ + int state; /* coroutine execution state */ + int is_cancelled; /* cancellation flag for structured concurrency */ + struct EpTask* parent; /* parent task for structured concurrency cancellation */ +}; + +/* Event Loop Scheduler Globals & Functions */ +static EpTask* ep_run_queue_head = NULL; +static EpTask* ep_run_queue_tail = NULL; +static EpTask* ep_current_task = NULL; +static int ep_event_loop_fd = -1; /* epoll or kqueue fd */ +static int ep_active_io_sources = 0; + +static void ep_task_enqueue(EpTask* task) { + if (!task) return; + task->next = NULL; + if (ep_run_queue_tail) { + ep_run_queue_tail->next = task; + ep_run_queue_tail = task; + } else { + ep_run_queue_head = ep_run_queue_tail = task; + } +} + +static EpTask* ep_task_dequeue(void) { + if (!ep_run_queue_head) return NULL; + EpTask* task = ep_run_queue_head; + ep_run_queue_head = ep_run_queue_head->next; + if (!ep_run_queue_head) ep_run_queue_tail = NULL; + return task; +} + +#ifndef __wasm__ +#ifdef __APPLE__ +#include +#else +#include +#endif +#endif + +static void ep_async_loop_init(void) { + if (ep_event_loop_fd != -1) return; +#ifdef __wasm__ + ep_event_loop_fd = 999; +#elif defined(__APPLE__) + ep_event_loop_fd = kqueue(); +#else + ep_event_loop_fd = epoll_create1(0); +#endif +} + +typedef struct EpTimer { + long long expiry_ms; + EpTask* task; + struct EpTimer* next; +} EpTimer; +static EpTimer* ep_timers_head = NULL; + +static void ep_async_register_timer(long long timeout_ms, EpTask* task) { + long long expiry = ep_time_now_ms() + timeout_ms; + EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer)); + timer->expiry_ms = expiry; + timer->task = task; + timer->next = NULL; + + /* Insert sorted */ + if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) { + timer->next = ep_timers_head; + ep_timers_head = timer; + } else { + EpTimer* cur = ep_timers_head; + while (cur->next && cur->next->expiry_ms <= expiry) { + cur = cur->next; + } + timer->next = cur->next; + cur->next = timer; + } +} + +static long long ep_get_next_timer_timeout(void) { + if (!ep_timers_head) return -1; /* block indefinitely */ + long long now = ep_time_now_ms(); + long long diff = ep_timers_head->expiry_ms - now; + return diff < 0 ? 0 : diff; +} + +static void ep_process_expired_timers(void) { + long long now = ep_time_now_ms(); + while (ep_timers_head && ep_timers_head->expiry_ms <= now) { + EpTimer* expired = ep_timers_head; + ep_timers_head = ep_timers_head->next; + ep_task_enqueue(expired->task); + free(expired); + } +} + +static void ep_async_register_read(int fd, EpTask* task) { +#ifdef __wasm__ + (void)fd; + (void)task; +#else + ep_async_loop_init(); + ep_active_io_sources++; +#ifdef __APPLE__ + struct kevent ev; + EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task); + kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL); +#else + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLONESHOT; + ev.data.ptr = task; + if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev); + } +#endif +#endif +} + +static void ep_async_wait_step(long long timeout) { +#ifdef __wasm__ + if (timeout > 0) { + ep_sleep_ms(timeout); + } +#else +#ifdef __APPLE__ + struct kevent events[16]; + struct timespec ts; + struct timespec* p_ts = NULL; + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + p_ts = &ts; + } + int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts); + for (int i = 0; i < n; i++) { + EpTask* t = (EpTask*)events[i].udata; + ep_task_enqueue(t); + ep_active_io_sources--; + } +#else + struct epoll_event events[16]; + int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout); + for (int i = 0; i < n; i++) { + EpTask* t = (EpTask*)events[i].data.ptr; + ep_task_enqueue(t); + ep_active_io_sources--; + } +#endif +#endif + ep_process_expired_timers(); +} + +static void ep_async_loop_run(void) { + ep_async_loop_init(); + while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) { + /* 1. Run all runnable tasks */ + while (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + continue; + } + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = NULL; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + + /* 2. If no tasks runnable, wait for I/O / timers */ + if (!ep_run_queue_head) { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + break; + } + + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + continue; + } + + ep_async_wait_step(timeout); + } + } +} + +static long long ep_await_future(EpFuture* fut) { + if (!fut) return 0; + while (!fut->completed) { + if (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task) { + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + } else { + EpTask* saved_current = ep_current_task; + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = saved_current; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + } + } else { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + fprintf(stderr, "Deadlock detected: awaiting incomplete future with no active tasks or timers.\n"); + exit(1); + } + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + } else { + ep_async_wait_step(timeout); + } + } + } + return fut->value; +} + +static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind); +long long create_list(void); +long long append_list(long long list_ptr, long long value); + +typedef struct { + EpFuture* futures[128]; + int count; + int has_error; +} EpTaskGroup; + +typedef struct { + EpFuture* fut; + int timer_fired; +} EpTimeoutArgs; + +static EpTask* ep_find_task_by_future(EpFuture* fut) { + if (!fut) return NULL; + EpTask* cur = ep_run_queue_head; + while (cur) { + if (cur->fut == fut) return cur; + cur = cur->next; + } + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task && timer->task->fut == fut) return timer->task; + timer = timer->next; + } + return NULL; +} + +static void ep_cancel_task(EpTask* task) { + if (!task) return; + task->is_cancelled = 1; + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + // Cancel children in run queue + EpTask* cur = ep_run_queue_head; + while (cur) { + if (cur->parent == task) { + ep_cancel_task(cur); + } + cur = cur->next; + } + // Cancel children in timers queue + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task && timer->task->parent == task) { + ep_cancel_task(timer->task); + } + timer = timer->next; + } +} + +static long long create_task_group(void) { + EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup)); + tg->count = 0; + tg->has_error = 0; + { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; } + return (long long)tg; +} + +static long long add_task_group(long long group_ptr, long long fut_ptr) { + EpTaskGroup* tg = (EpTaskGroup*)group_ptr; + EpFuture* fut = (EpFuture*)fut_ptr; + if (!tg || !fut) return 0; + if (tg->count < 128) { + tg->futures[tg->count++] = fut; + // Associate the task's parent with the current task so it's cancellation-linked + EpTask* task = ep_find_task_by_future(fut); + if (task) { + task->parent = ep_current_task; + } + } + return 0; +} + +static long long wait_task_group(long long group_ptr) { + EpTaskGroup* tg = (EpTaskGroup*)group_ptr; + if (!tg) return 0; + + int all_done = 0; + while (!all_done) { + all_done = 1; + for (int i = 0; i < tg->count; i++) { + EpFuture* fut = tg->futures[i]; + if (!fut->completed) { + all_done = 0; + break; + } + } + + if (all_done) break; + + if (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task) { + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + } else { + EpTask* saved_current = ep_current_task; + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = saved_current; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + } + } else { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n"); + exit(1); + } + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + } else { + ep_async_wait_step(timeout); + } + } + + // Propagate cancellation/failure inside task group + for (int i = 0; i < tg->count; i++) { + EpFuture* fut = tg->futures[i]; + if (fut->completed && fut->value == -1) { + tg->has_error = 1; + for (int j = 0; j < tg->count; j++) { + EpFuture* other_fut = tg->futures[j]; + if (!other_fut->completed) { + EpTask* other_task = ep_find_task_by_future(other_fut); + if (other_task) { + ep_cancel_task(other_task); + } else { + other_fut->completed = 1; + other_fut->value = -1; + } + } + } + } + } + } + + long long list = create_list(); + for (int i = 0; i < tg->count; i++) { + append_list(list, tg->futures[i]->value); + } + return list; +} + +static long long ep_timeout_timer_step(void* r) { + EpTimeoutArgs* args = (EpTimeoutArgs*)r; + if (args && args->fut && !args->fut->completed) { + args->timer_fired = 1; + EpTask* task = ep_find_task_by_future(args->fut); + if (task) { + ep_cancel_task(task); + } else { + args->fut->completed = 1; + args->fut->value = -1; + } + } + return 0; +} + +static long long async_timeout(long long timeout_ms, long long fut_ptr) { + EpFuture* fut = (EpFuture*)fut_ptr; + if (!fut) return -1; + if (fut->completed) return fut->value; + + EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs)); + args->fut = fut; + args->timer_fired = 0; + + EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask)); + timer_task->step = ep_timeout_timer_step; + timer_task->args = args; + timer_task->args_size_bytes = sizeof(EpTimeoutArgs); + timer_task->fut = NULL; + timer_task->state = 0; + timer_task->is_cancelled = 0; + timer_task->parent = NULL; + + ep_async_register_timer(timeout_ms, timer_task); + + while (!fut->completed && !(args->timer_fired)) { + if (ep_run_queue_head) { + EpTask* task = ep_task_dequeue(); + if (task) { + if (task->is_cancelled) { + if (task->fut) { + task->fut->completed = 1; + task->fut->value = -1; + } + free(task->args); + free(task); + } else { + EpTask* saved_current = ep_current_task; + ep_current_task = task; + long long res = task->step(task->args); + ep_current_task = saved_current; + if (res != -999999) { + if (task->fut) { + task->fut->value = res; + task->fut->completed = 1; + if (task->fut->waiting_task) { + ep_task_enqueue(task->fut->waiting_task); + task->fut->waiting_task = NULL; + } + } + free(task->args); + free(task); + } + } + } + } else { + long long timeout = ep_get_next_timer_timeout(); + if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { + break; + } + if (ep_event_loop_fd == -1) { + if (timeout > 0) { + ep_sleep_ms(timeout); + } + ep_process_expired_timers(); + } else { + ep_async_wait_step(timeout); + } + } + } + + return fut->value; +} + +/* ── Awaitable async socket-readability ───────────────────────────────────── + `await async_wait_readable(fd)` suspends the calling async task until `fd` is + readable, letting the event loop run other tasks (e.g. another agent waiting on + its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a + oneshot read-readiness task with the loop, return the future. When fd becomes + readable, ep_async_wait_step re-enqueues the task; its step completes the future + and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently + on ONE thread — no OS threads, no shared-heap GC race. */ +typedef struct { EpFuture* fut; } EpReadReadyArgs; +static long long ep_read_ready_step(void* r) { + EpReadReadyArgs* args = (EpReadReadyArgs*)r; + if (args && args->fut) { + args->fut->completed = 1; + args->fut->value = 1; + if (args->fut->waiting_task) { + ep_task_enqueue(args->fut->waiting_task); + args->fut->waiting_task = NULL; + } + } + return 0; +} +long long async_wait_readable(long long fd) { + EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture)); + fut->completed = 0; + fut->value = 0; + fut->waiting_task = NULL; + fut->chan = 0; + { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; } + EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs)); + args->fut = fut; + EpTask* task = (EpTask*)malloc(sizeof(EpTask)); + task->step = ep_read_ready_step; + task->args = args; + task->args_size_bytes = sizeof(EpReadReadyArgs); + task->fut = NULL; + task->state = 0; + task->is_cancelled = 0; + task->parent = ep_current_task; + ep_async_register_read((int)fd, task); + return (long long)fut; +} + +typedef struct { + EpFuture* fut; +} EpSleepTimerArgs; + +static long long ep_sleep_timer_step(void* r) { + EpSleepTimerArgs* args = (EpSleepTimerArgs*)r; + if (args && args->fut) { + args->fut->completed = 1; + args->fut->value = 0; + if (args->fut->waiting_task) { + ep_task_enqueue(args->fut->waiting_task); + args->fut->waiting_task = NULL; + } + } + return 0; +} + +static long long sleep_ms(long long ms) { + EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture)); + fut->completed = 0; + fut->value = 0; + fut->waiting_task = NULL; + fut->chan = 0; + { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; } + + EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs)); + args->fut = fut; + + EpTask* task = (EpTask*)malloc(sizeof(EpTask)); + task->step = ep_sleep_timer_step; + task->args = args; + task->args_size_bytes = sizeof(EpSleepTimerArgs); + task->fut = NULL; + task->state = 0; + task->is_cancelled = 0; + task->parent = ep_current_task; + + ep_async_register_timer(ms, task); + return (long long)fut; +} + +static long long cancel_task(long long fut_ptr) { + EpFuture* fut = (EpFuture*)fut_ptr; + if (fut) { + EpTask* task = ep_find_task_by_future(fut); + if (task) { + ep_cancel_task(task); + } else { + fut->completed = 1; + fut->value = -1; + } + } + return 0; +} + +/* Closure environment — captures travel with the function pointer */ +#define EP_CLOSURE_MAGIC 0x4550434C4FL +typedef struct { + long long magic; + long long fn_ptr; + long long env[]; /* flexible array of captured values */ +} EpClosure; + +/* GC globals */ +static EpGCObject* ep_gc_head = NULL; +static long long ep_gc_count = 0; +static long long ep_gc_threshold = 4096; +static int ep_gc_enabled = 1; +static long long ep_gc_nursery_count = 0; +static long long ep_gc_nursery_threshold = 512; +static int ep_gc_minor_count = 0; +static int ep_gc_major_count = 0; +static void** ep_gc_remembered_set = NULL; +static long long ep_gc_remembered_cap = 0; +static long long ep_gc_remembered_size = 0; +/* Single mutex for ALL GC and thread registry operations. + Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex) + which caused deadlock under concurrent channel load: thread A held gc_mutex + and waited for registry_mutex, thread B held registry_mutex and waited for + gc_mutex. Single lock eliminates the ordering problem. */ +#ifdef __wasm__ +#define __thread +#endif +static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in + ep_gc_stop_the_world(), waits until every *other* registered thread has parked + at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs + concurrently with a mutator changing its roots or an object's fields — the + "marking races with running mutators" hazard. All three fields are touched + only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at + safepoints are a benign optimization: a missed set just defers parking to the + next safepoint, and the collector's bounded wait covers it). */ +static volatile int ep_gc_stop_requested = 0; +static int ep_gc_parked_count = 0; +static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER; + +/* Function pointer for channel scanning — set after EpChannel is defined. + GC mark calls this to scan values in-transit in channel buffers. */ +static void (*ep_gc_scan_channels_major)(void) = NULL; +static void (*ep_gc_scan_channels_minor)(void) = NULL; +/* Function pointers for marking top-level constant/global variables, which are + GC roots that live outside any function frame. Set by __ep_init_constants. */ +static void (*ep_gc_mark_globals_major)(void) = NULL; +static void (*ep_gc_mark_globals_minor)(void) = NULL; +/* Function pointers for map value traversal — set after EpMap is defined. + GC mark calls these to recursively mark values stored in maps. */ +static void (*ep_gc_mark_map_values)(void* ptr) = NULL; +static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL; + +/* Thread registry for GC root scanning in multi-threaded environment */ +#define EP_MAX_THREADS 256 +static __thread void* volatile ep_thread_local_top = NULL; +static __thread void* ep_thread_local_bottom = NULL; + +static void* volatile* ep_thread_tops[EP_MAX_THREADS]; +static void* ep_thread_bottoms[EP_MAX_THREADS]; +static volatile int ep_thread_active[EP_MAX_THREADS]; +static int ep_num_threads = 0; + +/* Per-thread GC root state — heap-allocated, stable across thread lifetime. + Previous design stored raw pointers to __thread arrays (ep_gc_root_stack, + ep_gc_root_sp) in the global registry. When a thread exited, the __thread + storage was freed, leaving dangling pointers that ep_gc_mark would + dereference → segfault. Now each thread gets a heap-allocated state struct + that survives thread exit and is only recycled when the slot is reused. */ +typedef struct { + long long* roots[4096]; /* copy of root pointers, updated under lock */ + volatile int sp; /* current root stack pointer */ +} EpThreadGCState; + +static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS]; + +/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */ +#define EP_GC_MAX_ROOTS 4096 +static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS]; +static __thread int ep_gc_root_sp = 0; +static __thread int ep_thread_slot = -1; + +/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on + push and retreats on pop so that per-frame push/pop counts stay balanced. + Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further + roots are counted but not stored (those deep-overflow locals are simply not + traced) — crucially, we never overwrite or drop an outer frame's stored + roots, which the old "silently skip the push but still pop" path did. */ +static void ep_gc_push_root(long long* root) { + int idx = ep_gc_root_sp; + ep_gc_root_sp++; + if (idx < EP_GC_MAX_ROOTS) { + ep_gc_root_stack[idx] = root; + /* Update the heap-allocated state so GC mark can see it safely */ + if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) { + ep_thread_gc_states[ep_thread_slot]->roots[idx] = root; + ep_thread_gc_states[ep_thread_slot]->sp = + (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS; + } + } +} +static void ep_gc_pop_roots(long long count) { + ep_gc_root_sp -= (int)count; + if (ep_gc_root_sp < 0) ep_gc_root_sp = 0; + /* Update the heap-allocated state (clamped to the array bound) */ + if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) { + ep_thread_gc_states[ep_thread_slot]->sp = + (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS; + } +} + +/* Park the calling thread if the collector has stopped the world. + MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise + root set) is stable while parked, so the collector can scan it race-free. */ +static void ep_gc_park_if_stopped(void) { + if (!ep_gc_stop_requested) return; + /* Spill registers onto the stack and publish this thread's current stack top + so the collector can conservatively scan its frozen C stack while parked — + this catches roots held only in registers/temporaries that the precise + shadow stack does not yet record. _dummy is declared below _pregs, so its + (lower) address bounds a scan range that covers the spilled registers. */ + jmp_buf _pregs; + volatile char _top_marker; /* function-scope: stays valid while parked */ + memset(&_pregs, 0, sizeof(_pregs)); + setjmp(_pregs); + /* _top_marker is declared after _pregs, so its (lower) address bounds a scan + range [&_top_marker, stack_bottom] that covers the spilled registers. */ + ep_thread_local_top = (void*)&_top_marker; + __sync_synchronize(); /* publish shadow-stack + top writes before parking */ + ep_gc_parked_count++; + while (ep_gc_stop_requested) { + pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex); + } + ep_gc_parked_count--; +} + +/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held. + Waits (briefly releasing the lock so blocked mutators can reach a safepoint) + until all other registered threads have parked. After a bounded fallback + (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked + or idle with a stable shadow stack, so scanning it is still safe in practice. */ +static void ep_gc_stop_the_world(void) { + ep_gc_stop_requested = 1; + /* Actively-running threads reach a safepoint (every allocation and every + function entry) within microseconds, so they park on the first spin or + two. The bound only caps the rare case where a thread is blocked/idle + (e.g. just entered a channel op) and won't park — those have a stable + shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */ + for (int spins = 0; spins < 40; spins++) { + int others = 0; + for (int t = 0; t < ep_num_threads; t++) { + if (ep_thread_active[t] && t != ep_thread_slot) others++; + } + if (others <= 0 || ep_gc_parked_count >= others) return; + pthread_mutex_unlock(&ep_gc_mutex); +#ifdef _WIN32 + Sleep(1); +#elif !defined(__wasm__) + usleep(250); +#endif + pthread_mutex_lock(&ep_gc_mutex); + } +} + +/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */ +static void ep_gc_start_the_world(void) { + ep_gc_stop_requested = 0; + pthread_cond_broadcast(&ep_gc_resume_cond); +} + +static void ep_gc_register_thread(void* stack_bottom) { + ep_thread_local_bottom = stack_bottom; + ep_thread_local_top = stack_bottom; + + pthread_mutex_lock(&ep_gc_mutex); + int slot = -1; + for (int i = 0; i < ep_num_threads; i++) { + if (!ep_thread_active[i]) { + slot = i; + break; + } + } + if (slot == -1 && ep_num_threads < EP_MAX_THREADS) { + slot = ep_num_threads++; + } + if (slot != -1) { + ep_thread_tops[slot] = &ep_thread_local_top; + ep_thread_bottoms[slot] = stack_bottom; + /* Allocate or reuse heap state for this slot */ + if (!ep_thread_gc_states[slot]) { + ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState)); + } + ep_thread_gc_states[slot]->sp = 0; + ep_thread_slot = slot; + __sync_synchronize(); /* Memory barrier: state must be visible before active */ + ep_thread_active[slot] = 1; + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +static void ep_gc_unregister_thread(void) { + pthread_mutex_lock(&ep_gc_mutex); + for (int i = 0; i < ep_num_threads; i++) { + if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) { + /* Zero root count FIRST — even if ep_gc_mark races past the + active check, it will see sp=0 and walk no roots instead + of dereferencing stale __thread pointers */ + if (ep_thread_gc_states[i]) { + ep_thread_gc_states[i]->sp = 0; + } + __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */ + ep_thread_active[i] = 0; + ep_thread_slot = -1; + break; + } + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; } + +/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */ +typedef struct { + void* key; + EpGCObject* value; +} EpGCEntry; + +static EpGCEntry* ep_gc_table = NULL; +static long long ep_gc_table_cap = 0; +static long long ep_gc_table_size = 0; + +/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap) + with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4 + bits are always 0 and only every 16th bucket was ever a home slot. That caused + catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's + rehash became O(n^2), which (under the single global GC mutex) wedged the whole + node when a large object list was freed. A splitmix64 finalizer avalanches all + bits, so even the low bits taken by the (cap-1) mask are well distributed. */ +static inline long long ep_gc_index(void* key, long long cap) { + uint64_t z = (uint64_t)(uintptr_t)key; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; + z = z ^ (z >> 31); + return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */ +} + +/* Insert without growing — assumes a free slot exists. Used by the resize and by + ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of + the table mid-iteration. */ +static void ep_gc_table_place(void* key, EpGCObject* value) { + long long idx = ep_gc_index(key, ep_gc_table_cap); + while (ep_gc_table[idx].key != NULL) { + if (ep_gc_table[idx].key == key) { + ep_gc_table[idx].value = value; + return; + } + idx = (idx + 1) & (ep_gc_table_cap - 1); + } + ep_gc_table[idx].key = key; + ep_gc_table[idx].value = value; + ep_gc_table_size++; +} + +static void ep_gc_table_insert(void* key, EpGCObject* value) { + if (ep_gc_table_size * 2 >= ep_gc_table_cap) { + long long old_cap = ep_gc_table_cap; + long long new_cap = old_cap == 0 ? 512 : old_cap * 2; + EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry)); + EpGCEntry* old_table = ep_gc_table; + ep_gc_table = new_table; + ep_gc_table_cap = new_cap; + ep_gc_table_size = 0; + for (long long i = 0; i < old_cap; i++) { + if (old_table[i].key != NULL) { + ep_gc_table_place(old_table[i].key, old_table[i].value); + } + } + free(old_table); + } + ep_gc_table_place(key, value); +} + +static EpGCObject* ep_gc_table_get(void* key) { + if (ep_gc_table_cap == 0) return NULL; + long long idx = ep_gc_index(key, ep_gc_table_cap); + while (ep_gc_table[idx].key != NULL) { + if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value; + idx = (idx + 1) & (ep_gc_table_cap - 1); + } + return NULL; +} + +static void ep_gc_table_remove(void* key) { + if (ep_gc_table_cap == 0) return; + long long idx = ep_gc_index(key, ep_gc_table_cap); + while (ep_gc_table[idx].key != NULL) { + if (ep_gc_table[idx].key == key) { + ep_gc_table[idx].key = NULL; + ep_gc_table[idx].value = NULL; + ep_gc_table_size--; + /* Backward-shift rehash of the rest of this cluster. Re-place (no + resize: size is not growing) so a mid-iteration realloc can never + free the table out from under this loop. */ + long long next_idx = (idx + 1) & (ep_gc_table_cap - 1); + while (ep_gc_table[next_idx].key != NULL) { + void* rehash_key = ep_gc_table[next_idx].key; + EpGCObject* rehash_val = ep_gc_table[next_idx].value; + ep_gc_table[next_idx].key = NULL; + ep_gc_table[next_idx].value = NULL; + ep_gc_table_size--; + ep_gc_table_place(rehash_key, rehash_val); + next_idx = (next_idx + 1) & (ep_gc_table_cap - 1); + } + return; + } + idx = (idx + 1) & (ep_gc_table_cap - 1); + } +} + + + +/* Register a new GC object */ +static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) { + if (!ptr) return NULL; + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */ + EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject)); + if (!obj) { + pthread_mutex_unlock(&ep_gc_mutex); + return NULL; + } + obj->kind = kind; + obj->marked = 0; + obj->ptr = ptr; + obj->size = 0; + obj->num_fields = 0; + obj->generation = 0; + obj->next = ep_gc_head; + ep_gc_head = obj; + ep_gc_count++; + ep_gc_nursery_count++; + ep_gc_table_insert(ptr, obj); + pthread_mutex_unlock(&ep_gc_mutex); + return obj; +} + +/* Find GC object by pointer. + Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table + concurrently (from another thread's allocation). Mutator-side callers + (write barrier, free_struct/free_map/free_list, to-string) must use this + locking variant; code already holding the mutex (mark/sweep) calls + ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */ +static EpGCObject* ep_gc_find(void* ptr) { + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint */ + EpGCObject* obj = ep_gc_table_get(ptr); + pthread_mutex_unlock(&ep_gc_mutex); + return obj; +} + +/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0). + The whole operation runs under ep_gc_mutex so the table lookups and the + remembered-set update see a consistent table (no race with a concurrent + resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */ +static void ep_gc_write_barrier(void* host_ptr, long long val) { + if (val == 0) return; + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */ + EpGCObject* host_obj = ep_gc_table_get(host_ptr); + EpGCObject* val_obj = ep_gc_table_get((void*)val); + if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) { + /* Check if already in remembered set */ + int found = 0; + for (long long i = 0; i < ep_gc_remembered_size; i++) { + if (ep_gc_remembered_set[i] == (void*)val) { + found = 1; + break; + } + } + if (!found) { + if (ep_gc_remembered_size >= ep_gc_remembered_cap) { + long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2; + void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*)); + if (new_set) { + ep_gc_remembered_set = new_set; + ep_gc_remembered_cap = new_cap; + } + } + if (ep_gc_remembered_size < ep_gc_remembered_cap) { + ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val; + } + } + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +/* Forward declarations for list type (needed by GC mark) */ +typedef struct { + long long* data; + long long length; + long long capacity; +} EpList; + +/* A real heap object (list/map/string) is malloc'd, so its address is far above + the never-mapped first page. EP values that are NOT pointers — small ints, + booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096). + Guarding the object accessors with this turns "deref a non-pointer" (the cause + of the read_transcripts segfault, and that whole class) into a safe null return + instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */ +#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL) + +/* Mark a single object and recursively mark its children */ +static void ep_gc_mark_object(void* ptr) { + if (!ptr) return; + /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */ + EpGCObject* obj = ep_gc_table_get(ptr); + if (!obj || obj->marked) return; + obj->marked = 1; + + if (obj->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)ptr; + for (long long i = 0; i < list->length; i++) { + long long val = list->data[i]; + if (val != 0) { + ep_gc_mark_object((void*)val); + } + } + } else if (obj->kind == EP_OBJ_STRUCT) { + long long* fields = (long long*)ptr; + for (long long i = 0; i < obj->num_fields; i++) { + if (fields[i] != 0) { + ep_gc_mark_object((void*)fields[i]); + } + } + } else if (obj->kind == EP_OBJ_MAP) { + if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr); + } +} + +/* Mark a single object and recursively mark its children (only if it is Gen 0) */ +static void ep_gc_mark_object_minor(void* ptr) { + if (!ptr) return; + /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */ + EpGCObject* obj = ep_gc_table_get(ptr); + if (!obj || obj->generation != 0 || obj->marked) return; + obj->marked = 1; + + if (obj->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)ptr; + for (long long i = 0; i < list->length; i++) { + long long val = list->data[i]; + if (val != 0) { + ep_gc_mark_object_minor((void*)val); + } + } + } else if (obj->kind == EP_OBJ_STRUCT) { + long long* fields = (long long*)ptr; + for (long long i = 0; i < obj->num_fields; i++) { + if (fields[i] != 0) { + ep_gc_mark_object_minor((void*)fields[i]); + } + } + } else if (obj->kind == EP_OBJ_MAP) { + if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr); + } +} + +/* Conservatively scan every registered thread's C stack and mark any word that + looks like a tracked pointer. The collector spills its own registers and + publishes its top here; all other threads are parked at a safepoint with their + registers spilled and top published (ep_gc_park_if_stopped), so their stacks + are frozen. This complements the precise shadow stacks: it catches roots held + only in registers/temporaries (e.g. a freshly allocated object not yet stored + into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find. + + Only run on MAJOR collections: minor collections rely on the precise shadow + stacks plus the write barrier's remembered set (the standard generational + approach), so they do no stack scan at all — which means there is no racy + cross-thread stack read on the frequent minor path either. The expensive + full-stack scan is paid only on the rarer major collection, where it pins + any long-lived object reachable only via a register across many GCs. + + Marked no_sanitize_address: a conservative scan deliberately reads whole stack + ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */ +#if defined(__SANITIZE_ADDRESS__) +# define EP_NO_ASAN __attribute__((no_sanitize_address)) +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define EP_NO_ASAN __attribute__((no_sanitize_address)) +# endif +#endif +#ifndef EP_NO_ASAN +# define EP_NO_ASAN +#endif +EP_NO_ASAN +static void ep_gc_scan_thread_stacks(void) { + jmp_buf _regs; + volatile char _top_marker; + memset(&_regs, 0, sizeof(_regs)); + setjmp(_regs); /* spill the collector's own registers onto its stack */ + /* Publish the LOWEST of our own local addresses as this thread's live top, so the + scanned range covers both the stack marker and the register-spill buffer whatever + order the compiler laid them out (a missed _regs would drop a register-only root). */ + { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs; + ep_thread_local_top = (void*)((_a < _b) ? _a : _b); } + for (int t = 0; t < ep_num_threads; t++) { + if (!ep_thread_active[t]) continue; + if (!ep_thread_tops[t]) continue; + /* The published top comes from a char local, so it may not be pointer-aligned; + mask DOWN to 8 bytes. Aligning down only widens the conservative window by a + few harmless bytes — aligning up could skip the slot holding a live root. + Unaligned void** dereferences are UB and produce a skewed scan window on + strict platforms (caught by valgrind on Linux). */ + void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7); + void** end = (void**)ep_thread_bottoms[t]; + if (!start || !end) continue; + if (start > end) { void** tmp = start; start = end; end = tmp; } + for (void** cur = start; cur < end; cur++) { + void* p = *cur; + if (p) ep_gc_mark_object(p); + } + } +} + +/* Mark phase: traverse from ALL threads' explicit GC roots. + Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */ +static void ep_gc_mark(void) { + ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */ + for (int t = 0; t < ep_num_threads; t++) { + if (!ep_thread_active[t]) continue; + EpThreadGCState* state = ep_thread_gc_states[t]; + if (!state) continue; + int sp = state->sp; + if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue; + for (int i = 0; i < sp; i++) { + long long* root_ptr = state->roots[i]; + if (!root_ptr) continue; + long long val = *root_ptr; + if (val != 0) { + ep_gc_mark_object((void*)val); + } + } + } + /* Also mark from main thread's local root stack (thread 0 / unregistered) */ + int local_sp = ep_gc_root_sp; + if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS; + for (int i = 0; i < local_sp; i++) { + long long val = *ep_gc_root_stack[i]; + if (val != 0) { + ep_gc_mark_object((void*)val); + } + } + /* Mark active tasks in the scheduler run queue */ + EpTask* task = ep_run_queue_head; + while (task) { + if (task->fut) { + ep_gc_mark_object((void*)task->fut); + } + if (task->args && task->args_size_bytes > 0) { + long long* ptr = (long long*)task->args; + for (int i = 0; i < task->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object((void*)val); + } + } + task = task->next; + } + /* Mark active tasks in the timers queue */ + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task) { + EpTask* t = timer->task; + if (t->fut) { + ep_gc_mark_object((void*)t->fut); + } + if (t->args && t->args_size_bytes > 0) { + long long* ptr = (long long*)t->args; + for (int i = 0; i < t->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object((void*)val); + } + } + } + timer = timer->next; + } + /* Mark top-level constant/global variables (roots outside any frame) */ + if (ep_gc_mark_globals_major) ep_gc_mark_globals_major(); + /* Scan all registered channel buffers — values in-transit have no root */ + if (ep_gc_scan_channels_major) ep_gc_scan_channels_major(); +} + +/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it + finds. This closes a use-after-free on the frequent minor path: a freshly-allocated + argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating + h() — lives only on the C stack / in registers and is not yet on the precise shadow + stack, so a minor collection triggered mid-expression would otherwise free it. Scanning + ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap + (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by + ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held + roots onto the stack so the scan can see them. */ +EP_NO_ASAN +static void ep_gc_scan_own_stack_minor(void) { + jmp_buf _regs; + volatile char _marker; + memset(&_regs, 0, sizeof(_regs)); + setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */ + void* bottom = ep_thread_local_bottom; + if (!bottom) return; + /* Start at the LOWEST of our own local addresses so the scanned range covers both + the current stack top (_marker) and the register-spill buffer (_regs), regardless + of how the compiler ordered these locals on the stack. Missing _regs would drop a + root held only in a callee-saved register -> a rare use-after-free. */ + char* a = (char*)(void*)&_marker; + char* b = (char*)(void*)&_regs; + char* lo = (a < b) ? a : b; + /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8 + bytes. Aligning down only widens the conservative window by a few harmless + bytes — aligning up could skip the slot holding a live root. Unaligned void** + dereferences are UB and skew the scan window on strict platforms (valgrind). */ + void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7); + void** end = (void**)bottom; + if (start > end) { void** tmp = start; start = end; end = tmp; } + for (void** cur = start; cur < end; cur++) { + void* p = *cur; + if (p) ep_gc_mark_object_minor(p); + } +} + +static void ep_gc_mark_minor(void) { + /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument + temporaries (only on the stack / in registers, not yet on the shadow stack) that a + minor collection mid-expression would otherwise free. Own-thread only, so race-free. */ + ep_gc_scan_own_stack_minor(); + for (int t = 0; t < ep_num_threads; t++) { + if (!ep_thread_active[t]) continue; + EpThreadGCState* state = ep_thread_gc_states[t]; + if (!state) continue; + int sp = state->sp; + if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue; + for (int i = 0; i < sp; i++) { + long long* root_ptr = state->roots[i]; + if (!root_ptr) continue; + long long val = *root_ptr; + if (val != 0) { + ep_gc_mark_object_minor((void*)val); + } + } + } + int local_sp = ep_gc_root_sp; + if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS; + for (int i = 0; i < local_sp; i++) { + long long val = *ep_gc_root_stack[i]; + if (val != 0) { + ep_gc_mark_object_minor((void*)val); + } + } + /* Mark active tasks in the scheduler run queue for minor collection */ + EpTask* task = ep_run_queue_head; + while (task) { + if (task->fut) { + ep_gc_mark_object_minor((void*)task->fut); + } + if (task->args && task->args_size_bytes > 0) { + long long* ptr = (long long*)task->args; + for (int i = 0; i < task->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object_minor((void*)val); + } + } + task = task->next; + } + /* Mark active tasks in the timers queue for minor collection */ + EpTimer* timer = ep_timers_head; + while (timer) { + if (timer->task) { + EpTask* t = timer->task; + if (t->fut) { + ep_gc_mark_object_minor((void*)t->fut); + } + if (t->args && t->args_size_bytes > 0) { + long long* ptr = (long long*)t->args; + for (int i = 0; i < t->args_size_bytes / 8; i++) { + long long val = ptr[i]; + if (val != 0) ep_gc_mark_object_minor((void*)val); + } + } + } + timer = timer->next; + } + /* Also mark from the remembered set */ + for (long long i = 0; i < ep_gc_remembered_size; i++) { + ep_gc_mark_object_minor(ep_gc_remembered_set[i]); + } + /* Mark top-level constant/global variables (roots outside any frame) */ + if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor(); + /* Scan all registered channel buffers — values in-transit have no root */ + if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor(); +} + +static void ep_gc_sweep_minor(void) { + EpGCObject** cur = &ep_gc_head; + while (*cur) { + if ((*cur)->generation == 0) { + if (!(*cur)->marked) { + EpGCObject* garbage = *cur; + *cur = garbage->next; + ep_gc_table_remove(garbage->ptr); + if (garbage->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)garbage->ptr; + if (list) { + free(list->data); + free(list); + } + } else if (garbage->kind == EP_OBJ_STRING) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_STRUCT) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_CLOSURE) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_MAP) { + /* EpMap layout: entries*, capacity, size. Free entries then map. */ + void** map_fields = (void**)garbage->ptr; + if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */ + free(garbage->ptr); + } + free(garbage); + ep_gc_count--; + ep_gc_nursery_count--; + } else { + (*cur)->marked = 0; + (*cur)->generation = 1; + ep_gc_nursery_count--; + cur = &(*cur)->next; + } + } else { + cur = &(*cur)->next; + } + } + ep_gc_remembered_size = 0; +} + +static void ep_gc_sweep_major(void) { + EpGCObject** cur = &ep_gc_head; + while (*cur) { + if (!(*cur)->marked) { + EpGCObject* garbage = *cur; + *cur = garbage->next; + ep_gc_table_remove(garbage->ptr); + if (garbage->generation == 0) { + ep_gc_nursery_count--; + } + if (garbage->kind == EP_OBJ_LIST) { + EpList* list = (EpList*)garbage->ptr; + if (list) { + free(list->data); + free(list); + } + } else if (garbage->kind == EP_OBJ_STRING) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_STRUCT) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_CLOSURE) { + free(garbage->ptr); + } else if (garbage->kind == EP_OBJ_MAP) { + void** map_fields = (void**)garbage->ptr; + if (map_fields && map_fields[0]) free(map_fields[0]); + free(garbage->ptr); + } + free(garbage); + ep_gc_count--; + } else { + (*cur)->marked = 0; + if ((*cur)->generation == 0) { + (*cur)->generation = 1; + ep_gc_nursery_count--; + } + cur = &(*cur)->next; + } + } + ep_gc_remembered_size = 0; +} + +static void ep_gc_collect_minor(void) { + if (!ep_gc_enabled) return; + ep_gc_minor_count++; + ep_gc_mark_minor(); + ep_gc_sweep_minor(); +} + +static void ep_gc_collect_major(void) { + if (!ep_gc_enabled) return; + ep_gc_major_count++; + ep_gc_mark(); + ep_gc_sweep_major(); + ep_gc_threshold = ep_gc_count * 2; + if (ep_gc_threshold < 4096) ep_gc_threshold = 4096; +} + +/* Run a full GC collection — caller MUST hold ep_gc_mutex */ +static void ep_gc_collect(void) { + ep_gc_collect_major(); +} + +/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function + GC safepoint: if another thread has stopped the world, park here until it's done. */ +static void ep_gc_maybe_collect(void) { + if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */ + /* Safepoint: lock-free fast check, then park under the lock if a collection + is in progress on another thread. Keeps the no-GC path lock-free. */ + if (ep_gc_stop_requested) { + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); + pthread_mutex_unlock(&ep_gc_mutex); + } + /* Fast path: check thresholds before acquiring mutex. + Counters are only incremented under the mutex, so worst case + we miss one collection cycle — safe trade-off for avoiding + a mutex lock/unlock (~20-50ns) on every function call. */ + if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return; + EP_GC_UPDATE_TOP(); + pthread_mutex_lock(&ep_gc_mutex); + /* Another thread may have started collecting between the check and the lock — + park instead of racing it, then re-check thresholds under the lock. */ + ep_gc_park_if_stopped(); + if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) { + ep_gc_stop_the_world(); + if (ep_gc_nursery_count >= ep_gc_nursery_threshold) { + ep_gc_collect_minor(); + } + if (ep_gc_count >= ep_gc_threshold) { + ep_gc_collect_major(); + } + ep_gc_start_the_world(); + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +/* Unregister an object (for explicit free — removes from GC tracking) */ +static void ep_gc_unregister(void* ptr) { + if (!ptr) return; + pthread_mutex_lock(&ep_gc_mutex); + ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */ + /* Clean up references from the remembered set to prevent dangling pointers */ + for (long long i = 0; i < ep_gc_remembered_size; ) { + if (ep_gc_remembered_set[i] == ptr) { + for (long long j = i; j < ep_gc_remembered_size - 1; j++) { + ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1]; + } + ep_gc_remembered_size--; + } else { + i++; + } + } + ep_gc_table_remove(ptr); + EpGCObject** cur = &ep_gc_head; + while (*cur) { + if ((*cur)->ptr == ptr) { + EpGCObject* found = *cur; + *cur = found->next; + if (found->generation == 0) { + ep_gc_nursery_count--; + } + free(found); + ep_gc_count--; + pthread_mutex_unlock(&ep_gc_mutex); + return; + } + cur = &(*cur)->next; + } + pthread_mutex_unlock(&ep_gc_mutex); +} + +/* Cleanup all remaining GC objects (called at program exit) */ +static void ep_gc_shutdown(void) { + ep_gc_enabled = 0; + /* Only free GC bookkeeping structures, not the tracked objects themselves. + The RAII auto-cleanup has already freed owned objects, and the OS will + reclaim everything else on process exit. Attempting to free individual + objects here causes double-free aborts when RAII and GC both track + the same allocation. */ + EpGCObject* cur = ep_gc_head; + while (cur) { + EpGCObject* next = cur->next; + free(cur); /* free the GCObject wrapper only */ + cur = next; + } + ep_gc_head = NULL; + ep_gc_count = 0; + if (ep_gc_table) { + free(ep_gc_table); + ep_gc_table = NULL; + } + ep_gc_table_cap = 0; + ep_gc_table_size = 0; +} + +/* ========== End Garbage Collector ========== */ + +long long create_list(void); +long long append_list(long long list_ptr, long long value); +long long get_list(long long list_ptr, long long index); +long long set_list(long long list_ptr, long long index, long long value); +long long length_list(long long list_ptr); +long long free_list(long long list_ptr); +long long pop_list(long long list_ptr); +long long remove_list(long long list_ptr, long long index); +char* string_from_list(long long list_ptr); +long long string_to_list(const char* s); +long long string_length(const char* s); +long long display_string(const char* s); +long long file_read(long long path_val); +long long file_write(long long path_val, long long content_val); +long long file_append(long long path_val, long long content_val); +long long file_exists(long long path_val); +long long string_contains(long long s_val, long long sub_val); +long long string_index_of(long long s_val, long long sub_val); +long long string_replace(long long s_val, long long old_val, long long new_val); +long long string_upper(long long s_val); +long long string_lower(long long s_val); +long long string_trim(long long s_val); +long long string_split(long long s_val, long long delim_val); +long long char_at(long long s_val, long long index); +long long char_from_code(long long code); +long long ep_abs(long long n); +long long json_get_string(long long json_val, long long key_val); +long long json_get_int(long long json_val, long long key_val); +long long json_get_bool(long long json_val, long long key_val); +long long ep_sha1(long long data_val); +long long ep_net_recv_bytes(long long fd, long long count); +long long channel_try_recv(long long chan_ptr, long long out_ptr); +long long channel_has_data(long long chan_ptr); +long long channel_select(long long channels_list, long long timeout_ms); +long long ep_auto_to_string(long long val); + +typedef struct EpChannel_ { + long long* data; + long long capacity; + long long head; + long long tail; + long long size; + ep_mutex_t mutex; + ep_cond_t cond_recv; + ep_cond_t cond_send; +} EpChannel; + +/* Global channel registry — allows GC to scan values in-transit in channel buffers. + Without this, an object sent to a channel but not yet received has NO GC root: + the sender has popped it, the receiver hasn't pushed it, and the channel buffer + is not scanned. The GC sweeps it → receiver gets a dangling pointer. */ +#define EP_MAX_CHANNELS 1024 +static EpChannel* ep_channel_registry[EP_MAX_CHANNELS]; +static int ep_channel_count = 0; +static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void ep_register_channel(EpChannel* chan) { + pthread_mutex_lock(&ep_channel_registry_mutex); + if (ep_channel_count < EP_MAX_CHANNELS) { + ep_channel_registry[ep_channel_count++] = chan; + } + pthread_mutex_unlock(&ep_channel_registry_mutex); +} + +/* Channel scanning implementations — called by GC mark via function pointers. + These are defined here (after EpChannel) so they can access struct fields. */ +static void ep_gc_mark_object(void* ptr); /* forward decl */ +static void ep_gc_mark_object_minor(void* ptr); /* forward decl */ + +static void ep_gc_scan_channels_major_impl(void) { + pthread_mutex_lock(&ep_channel_registry_mutex); + for (int c = 0; c < ep_channel_count; c++) { + EpChannel* chan = ep_channel_registry[c]; + if (!chan || chan->size <= 0) continue; + ep_mutex_lock(&chan->mutex); + for (long long j = 0; j < chan->size; j++) { + long long idx = (chan->head + j) % chan->capacity; + long long val = chan->data[idx]; + if (val != 0) ep_gc_mark_object((void*)val); + } + ep_mutex_unlock(&chan->mutex); + } + pthread_mutex_unlock(&ep_channel_registry_mutex); +} + +static void ep_gc_scan_channels_minor_impl(void) { + pthread_mutex_lock(&ep_channel_registry_mutex); + for (int c = 0; c < ep_channel_count; c++) { + EpChannel* chan = ep_channel_registry[c]; + if (!chan || chan->size <= 0) continue; + ep_mutex_lock(&chan->mutex); + for (long long j = 0; j < chan->size; j++) { + long long idx = (chan->head + j) % chan->capacity; + long long val = chan->data[idx]; + if (val != 0) ep_gc_mark_object_minor((void*)val); + } + ep_mutex_unlock(&chan->mutex); + } + pthread_mutex_unlock(&ep_channel_registry_mutex); +} + +long long create_channel(void) { + EpChannel* chan = malloc(sizeof(EpChannel)); + if (!chan) return 0; + chan->capacity = 1024; + chan->data = malloc(chan->capacity * sizeof(long long)); + chan->head = 0; + chan->tail = 0; + chan->size = 0; + ep_mutex_init(&chan->mutex); + ep_cond_init(&chan->cond_recv); + ep_cond_init(&chan->cond_send); + ep_register_channel(chan); + return (long long)chan; +} + +long long send_channel(long long chan_ptr, long long value) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + /* Suppress GC during channel operations. The blocking condvar wait + can interleave with GC mark/sweep on another thread, causing + use-after-free when the GC sweeps objects that are live on a + thread currently blocked in send/receive. Channel buffers contain + raw long long values (not GC-tracked pointers), so suppressing + GC here is safe. */ + int gc_was_enabled = ep_gc_enabled; + ep_gc_enabled = 0; + ep_mutex_lock(&chan->mutex); + while (chan->size >= chan->capacity) { + ep_cond_wait(&chan->cond_send, &chan->mutex); + } + chan->data[chan->tail] = value; + chan->tail = (chan->tail + 1) % chan->capacity; + chan->size += 1; + ep_cond_signal(&chan->cond_recv); + ep_mutex_unlock(&chan->mutex); + ep_gc_enabled = gc_was_enabled; + return value; +} + +long long receive_channel(long long chan_ptr) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + /* Suppress GC during channel receive — same rationale as send_channel */ + int gc_was_enabled = ep_gc_enabled; + ep_gc_enabled = 0; + ep_mutex_lock(&chan->mutex); + while (chan->size <= 0) { + ep_cond_wait(&chan->cond_recv, &chan->mutex); + } + long long value = chan->data[chan->head]; + chan->head = (chan->head + 1) % chan->capacity; + chan->size -= 1; + ep_cond_signal(&chan->cond_send); + ep_mutex_unlock(&chan->mutex); + ep_gc_enabled = gc_was_enabled; + return value; +} + +// Non-blocking receive — returns 1 if data was available, 0 if channel empty +long long channel_try_recv(long long chan_ptr, long long out_ptr) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + ep_mutex_lock(&chan->mutex); + if (chan->size <= 0) { + ep_mutex_unlock(&chan->mutex); + return 0; + } + long long value = chan->data[chan->head]; + chan->head = (chan->head + 1) % chan->capacity; + chan->size -= 1; + ep_cond_signal(&chan->cond_send); + ep_mutex_unlock(&chan->mutex); + if (out_ptr) { + *((long long*)out_ptr) = value; + } + return 1; +} + +// Check if channel has data without consuming it +long long channel_has_data(long long chan_ptr) { + EpChannel* chan = (EpChannel*)chan_ptr; + if (!chan) return 0; + ep_mutex_lock(&chan->mutex); + int has = (chan->size > 0) ? 1 : 0; + ep_mutex_unlock(&chan->mutex); + return has; +} + +// Select: wait for any of N channels to have data, with timeout in ms +// channels_list is a list of channel pointers +// Returns index (0-based) of first ready channel, or -1 on timeout +long long channel_select(long long channels_list, long long timeout_ms) { + EpList* list = (EpList*)channels_list; + if (!list || list->length == 0) return -1; + +#ifdef _WIN32 + ULONGLONG start_tick = GetTickCount64(); +#else + struct timespec start, now; + clock_gettime(CLOCK_MONOTONIC, &start); +#endif + + while (1) { + // Poll all channels + for (long long i = 0; i < list->length; i++) { + EpChannel* chan = (EpChannel*)list->data[i]; + if (chan) { + ep_mutex_lock(&chan->mutex); + if (chan->size > 0) { + ep_mutex_unlock(&chan->mutex); + return i; + } + ep_mutex_unlock(&chan->mutex); + } + } + + // Check timeout + if (timeout_ms >= 0) { +#ifdef _WIN32 + ULONGLONG now_tick = GetTickCount64(); + long long elapsed = (long long)(now_tick - start_tick); +#else + clock_gettime(CLOCK_MONOTONIC, &now); + long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000; +#endif + if (elapsed >= timeout_ms) return -1; + } + + // Brief sleep to avoid busy-wait +#ifdef _WIN32 + Sleep(1); +#else + usleep(1000); // 1ms +#endif + } +} + +#ifdef __wasm__ +long long ep_net_connect(const char* host, long long port) { + (void)host; (void)port; + return -1; +} + +long long ep_net_listen(long long port) { + (void)port; + return -1; +} + +long long ep_net_accept(long long server_fd) { + (void)server_fd; + return -1; +} + +long long ep_net_send(long long fd, const char* data) { + (void)fd; (void)data; + return 0; +} + +char* ep_net_recv(long long fd, long long max_len) { + (void)fd; (void)max_len; + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + return empty; +} + +long long ep_net_close(long long fd) { + (void)fd; + return -1; +} + +long long ep_sleep_ms(long long ms) { + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&ts, NULL); + return 0; +} + +long long ep_system(long long cmd) { + (void)cmd; + return -1; +} + +long long ep_play_sound(long long path) { + (void)path; + return -1; +} + +long long ep_dlopen(long long path) { + (void)path; + return 0; +} + +long long ep_dlsym(long long handle, long long name) { + (void)handle; (void)name; + return 0; +} + +long long ep_dlclose(long long handle) { + (void)handle; + return 0; +} +#else +long long ep_net_connect(const char* host, long long port) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) return -1; + struct hostent* server = gethostbyname(host); + if (!server) { +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + return -1; + } + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length); + serv_addr.sin_port = htons(port); +#ifdef _WIN32 + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + closesocket(sockfd); + return -1; + } +#else + // Bounded connect: an unreachable peer must not block ~75s on the OS SYN + // timeout (this stalled node startup). Non-blocking connect + 5s select, then + // restore blocking mode for the rest of the session. + int _ep_flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK); + int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + if (_ep_cr < 0) { + if (errno != EINPROGRESS) { close(sockfd); return -1; } + fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset); + struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0; + int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv); + if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error + int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) { + close(sockfd); + return -1; + } + } + fcntl(sockfd, F_SETFL, _ep_flags); +#endif + return sockfd; +} + +long long ep_net_listen(long long port) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) return -1; + int opt = 1; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + return -1; + } + if (listen(sockfd, 10) < 0) { +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + return -1; + } + return sockfd; +} + +long long ep_net_accept(long long server_fd) { + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen); + if (newsockfd >= 0) { + /* Bound how long a single recv/send may block so a slow or silent + client cannot pin a handler thread forever (slowloris). */ + struct timeval tv; + tv.tv_sec = 30; + tv.tv_usec = 0; + setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); + setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); + } + return newsockfd; +} + +long long ep_net_send(long long fd, const char* data) { + if (!data) return 0; + /* send() may write fewer bytes than requested (partial write under load/ + backpressure). A single send() therefore silently truncated large IPC + responses, cutting agent replies mid-stream. Loop until all bytes are sent. */ + size_t total = strlen(data); + size_t off = 0; + while (off < total) { + ssize_t n = send((int)fd, data + off, total - off, 0); + if (n <= 0) break; + off += (size_t)n; + } + return (long long)off; +} + +char* ep_net_recv(long long fd, long long max_len) { + char* buf = malloc(max_len + 1); + if (!buf) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + return empty; + } +#ifdef _WIN32 + int n = recv((int)fd, buf, (int)max_len, 0); +#else + ssize_t n = recv((int)fd, buf, max_len, 0); +#endif + if (n < 0) n = 0; + buf[n] = '\0'; + return buf; +} + +long long ep_net_close(long long fd) { +#ifdef _WIN32 + return closesocket((int)fd); +#else + return close((int)fd); +#endif +} + +long long ep_sleep_ms(long long ms) { +#ifdef _WIN32 + Sleep((DWORD)ms); +#else + usleep((useconds_t)(ms * 1000)); +#endif + return 0; +} + +long long ep_system(long long cmd) { + return (long long)system((const char*)cmd); +} + +long long ep_play_sound(long long path) { + char cmd[512]; + snprintf(cmd, sizeof(cmd), "afplay '%s' &", (const char*)path); + return (long long)system(cmd); +} + +/* ========== Dynamic Library Loading (FFI) ========== */ +#ifndef _WIN32 +#include +#endif + +long long ep_dlopen(long long path) { +#ifdef _WIN32 + HMODULE h = LoadLibraryA((const char*)path); + return (long long)h; +#else + const char* p = (const char*)path; + void* handle = dlopen(p, RTLD_LAZY); + return (long long)handle; +#endif +} + +long long ep_dlsym(long long handle, long long name) { +#ifdef _WIN32 + FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name); + return (long long)sym; +#else + void* sym = dlsym((void*)handle, (const char*)name); + return (long long)sym; +#endif +} + +long long ep_dlclose(long long handle) { +#ifdef _WIN32 + return (long long)FreeLibrary((HMODULE)handle); +#else + return (long long)dlclose((void*)handle); +#endif +} +#endif + +/* Call a function pointer with 0..6 arguments. + These are type-punned through long long — the C calling convention + makes this work for integer and pointer arguments. */ +typedef long long (*ep_fn0)(void); +typedef long long (*ep_fn1)(long long); +typedef long long (*ep_fn2)(long long, long long); +typedef long long (*ep_fn3)(long long, long long, long long); +typedef long long (*ep_fn4)(long long, long long, long long, long long); +typedef long long (*ep_fn5)(long long, long long, long long, long long, long long); +typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long); +typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long); + +long long ep_dlcall0(long long fptr) { + return ((ep_fn0)fptr)(); +} +long long ep_dlcall1(long long fptr, long long a0) { + return ((ep_fn1)fptr)(a0); +} +long long ep_dlcall2(long long fptr, long long a0, long long a1) { + return ((ep_fn2)fptr)(a0, a1); +} +long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) { + return ((ep_fn3)fptr)(a0, a1, a2); +} +long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) { + return ((ep_fn4)fptr)(a0, a1, a2, a3); +} +long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) { + return ((ep_fn5)fptr)(a0, a1, a2, a3, a4); +} +long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) { + return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5); +} +long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) { + return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6); +} +long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) { + return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7); +} +long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) { + return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8); +} +long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) { + return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); +} + +/* ========== Float FFI: ep_dlcall_f* ========== */ +/* For calling C functions that accept/return double values. + Arguments are passed as long long (bit-punned doubles). + Return value is a double bit-punned back to long long. + Use ep_double_to_bits() / ep_bits_to_double() to convert. */ + +typedef union { long long i; double f; } ep_float_bits; + +static inline double ep_ll_to_double(long long v) { + ep_float_bits u; u.i = v; return u.f; +} +static inline long long ep_double_to_ll(double v) { + ep_float_bits u; u.f = v; return u.i; +} + +/* Convert between ErnosPlain float representation and raw bits */ +long long ep_double_to_bits(long long float_val) { + /* float_val is already an EP Float stored as long long bits */ + return float_val; +} +long long ep_bits_to_double(long long bits) { + return bits; +} + +/* Float function pointer typedefs */ +typedef double (*ep_ff0)(void); +typedef double (*ep_ff1)(double); +typedef double (*ep_ff2)(double, double); +typedef double (*ep_ff3)(double, double, double); +typedef double (*ep_ff4)(double, double, double, double); +typedef double (*ep_ff5)(double, double, double, double, double); +typedef double (*ep_ff6)(double, double, double, double, double, double); + +/* Call functions that take doubles and return double */ +long long ep_dlcall_f0(long long fptr) { + return ep_double_to_ll(((ep_ff0)fptr)()); +} +long long ep_dlcall_f1(long long fptr, long long a0) { + return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0))); +} +long long ep_dlcall_f2(long long fptr, long long a0, long long a1) { + return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1))); +} +long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) { + return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2))); +} +long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) { + return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3))); +} +long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) { + return ep_double_to_ll(((ep_ff5)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4))); +} +long long ep_dlcall_f6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) { + return ep_double_to_ll(((ep_ff6)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4), ep_ll_to_double(a5))); +} + +/* Variants that take doubles but return int (for comparison functions etc.) */ +typedef long long (*ep_fdi1)(double); +typedef long long (*ep_fdi2)(double, double); +typedef long long (*ep_fdi3)(double, double, double); + +long long ep_dlcall_fd1(long long fptr, long long a0) { + return ((ep_fdi1)fptr)(ep_ll_to_double(a0)); +} +long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) { + return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)); +} +long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) { + return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)); +} +/* ========== End Float FFI ========== */ +/* ========== End Dynamic Library Loading ========== */ + +unsigned long hash_string(const char* str) { + unsigned long hash = 5381; + int c; + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; + } + return hash; +} + +typedef struct { + char* key; + long long value; + int used; +} EpMapEntry; + +typedef struct { + EpMapEntry* entries; + long long capacity; + long long size; +} EpMap; + +/* Map value traversal for GC — walks all entries and marks values. + Called by ep_gc_mark_object() via function pointer. */ +static void ep_gc_mark_map_values_impl(void* ptr) { + EpMap* map = (EpMap*)ptr; + if (!map || !map->entries) return; + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].value != 0) { + ep_gc_mark_object((void*)map->entries[i].value); + } + /* Also mark keys if they are heap strings */ + if (map->entries[i].used && map->entries[i].key != NULL) { + ep_gc_mark_object((void*)map->entries[i].key); + } + } +} + +static void ep_gc_mark_map_values_minor_impl(void* ptr) { + EpMap* map = (EpMap*)ptr; + if (!map || !map->entries) return; + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].value != 0) { + ep_gc_mark_object_minor((void*)map->entries[i].value); + } + if (map->entries[i].used && map->entries[i].key != NULL) { + ep_gc_mark_object_minor((void*)map->entries[i].key); + } + } +} + +long long create_map(void) { + EpMap* map = malloc(sizeof(EpMap)); + if (!map) return 0; + map->capacity = 16; + map->size = 0; + map->entries = calloc(map->capacity, sizeof(EpMapEntry)); + if (!map->entries) { + free(map); + return 0; + } + ep_gc_register(map, EP_OBJ_MAP); + return (long long)map; +} + +static void map_resize(EpMap* map, long long new_capacity) { + EpMapEntry* old_entries = map->entries; + long long old_capacity = map->capacity; + map->capacity = new_capacity; + map->entries = calloc(new_capacity, sizeof(EpMapEntry)); + map->size = 0; + for (long long i = 0; i < old_capacity; i++) { + if (old_entries[i].used && old_entries[i].key != NULL) { + char* key = old_entries[i].key; + long long value = old_entries[i].value; + unsigned long h = hash_string(key) % new_capacity; + while (map->entries[h].used) { + h = (h + 1) % new_capacity; + } + map->entries[h].key = key; + map->entries[h].value = value; + map->entries[h].used = 1; + map->size++; + } + } + free(old_entries); +} + +/* Convert a key value to a string — handles both string pointers and integers */ +static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) { + if (key_val == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; } + /* Check if value is in plausible pointer range for a string */ + if (key_val > 0x100000) { + const char* p = (const char*)(void*)key_val; + unsigned char first = (unsigned char)*p; + if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) { + return p; /* valid string pointer */ + } + } + snprintf(buf, bufsize, "%lld", key_val); + return buf; +} + +long long map_insert(long long map_ptr, long long key_val, long long value) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + if (map->size * 2 >= map->capacity) { + map_resize(map, map->capacity * 2); + } + unsigned long h = hash_string(key) % map->capacity; + while (map->entries[h].used) { + if (strcmp(map->entries[h].key, key) == 0) { + map->entries[h].value = value; + ep_gc_write_barrier((void*)map_ptr, value); + return value; + } + h = (h + 1) % map->capacity; + } + map->entries[h].key = strdup(key); + map->entries[h].value = value; + map->entries[h].used = 1; + map->size++; + ep_gc_write_barrier((void*)map_ptr, value); + return value; +} + +long long map_get_val(long long map_ptr, long long key_val) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + unsigned long h = hash_string(key) % map->capacity; + long long start_h = h; + while (map->entries[h].used) { + if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { + return map->entries[h].value; + } + h = (h + 1) % map->capacity; + if (h == start_h) break; + } + return 0; +} + +/* map_set_str: store a string value (strdup'd copy) under a string key */ +long long map_set_str(long long map_ptr, long long key_val, long long str_val) { + /* Store the string pointer as a long long value — same as map_insert */ + return map_insert(map_ptr, key_val, str_val); +} + +/* map_get_str: retrieve a string value from a map (returns char* as long long) */ +long long map_get_str(long long map_ptr, long long key_val) { + /* Same as map_get_val — the stored long long IS a char* pointer */ + return map_get_val(map_ptr, key_val); +} + +long long map_contains(long long map_ptr, long long key_val) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + unsigned long h = hash_string(key) % map->capacity; + long long start_h = h; + while (map->entries[h].used) { + if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { + return 1; + } + h = (h + 1) % map->capacity; + if (h == start_h) break; + } + return 0; +} + +long long map_delete(long long map_ptr, long long key_val) { + if (EP_BADPTR(map_ptr)) return 0; + EpMap* map = (EpMap*)map_ptr; + char keybuf[32]; + const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf)); + if (!map) return 0; + unsigned long h = hash_string(key) % map->capacity; + long long start_h = h; + while (map->entries[h].used) { + if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) { + free(map->entries[h].key); + map->entries[h].key = NULL; + map->entries[h].value = 0; + map->entries[h].used = 0; + map->size--; + long long next_h = (h + 1) % map->capacity; + while (map->entries[next_h].used) { + char* k = map->entries[next_h].key; + long long v = map->entries[next_h].value; + map->entries[next_h].key = NULL; + map->entries[next_h].value = 0; + map->entries[next_h].used = 0; + map->size--; + map_insert(map_ptr, (long long)k, v); + free(k); + next_h = (next_h + 1) % map->capacity; + } + return 1; + } + h = (h + 1) % map->capacity; + if (h == start_h) break; + } + return 0; +} + +long long map_keys(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return (long long)create_list(); + long long list = create_list(); + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].key) { + append_list(list, (long long)strdup(map->entries[i].key)); + } + } + return list; +} + +long long map_values(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return (long long)create_list(); + long long list = create_list(); + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].key) { + append_list(list, map->entries[i].value); + } + } + return list; +} + +long long map_size(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return 0; + return map->size; +} + +long long free_map(long long map_ptr) { + EpMap* map = (EpMap*)map_ptr; + if (!map) return 0; + /* Skip if already freed (idempotent) */ + if (!ep_gc_find(map)) return 0; + ep_gc_unregister(map); + for (long long i = 0; i < map->capacity; i++) { + if (map->entries[i].used && map->entries[i].key != NULL) { + free(map->entries[i].key); + } + } + free(map->entries); + free(map); + return 0; +} + +typedef struct { + long long* data; + long long capacity; + long long head; + long long tail; + long long size; +} EpDeque; + +long long create_deque(void) { + EpDeque* dq = malloc(sizeof(EpDeque)); + if (!dq) return 0; + dq->capacity = 16; + dq->size = 0; + dq->head = 0; + dq->tail = 0; + dq->data = malloc(dq->capacity * sizeof(long long)); + if (!dq->data) { + free(dq); + return 0; + } + return (long long)dq; +} + +static void deque_resize(EpDeque* dq, long long new_capacity) { + long long* new_data = malloc(new_capacity * sizeof(long long)); + for (long long i = 0; i < dq->size; i++) { + new_data[i] = dq->data[(dq->head + i) % dq->capacity]; + } + free(dq->data); + dq->data = new_data; + dq->capacity = new_capacity; + dq->head = 0; + dq->tail = dq->size; +} + +long long deque_push_back(long long dq_ptr, long long value) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + if (dq->size >= dq->capacity) { + deque_resize(dq, dq->capacity * 2); + } + dq->data[dq->tail] = value; + dq->tail = (dq->tail + 1) % dq->capacity; + dq->size++; + return value; +} + +long long deque_push_front(long long dq_ptr, long long value) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + if (dq->size >= dq->capacity) { + deque_resize(dq, dq->capacity * 2); + } + dq->head = (dq->head - 1 + dq->capacity) % dq->capacity; + dq->data[dq->head] = value; + dq->size++; + return value; +} + +long long deque_pop_back(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq || dq->size == 0) return 0; + dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity; + long long value = dq->data[dq->tail]; + dq->size--; + return value; +} + +long long deque_pop_front(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq || dq->size == 0) return 0; + long long value = dq->data[dq->head]; + dq->head = (dq->head + 1) % dq->capacity; + dq->size--; + return value; +} + +long long deque_length(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + return dq->size; +} + +long long free_deque(long long dq_ptr) { + EpDeque* dq = (EpDeque*)dq_ptr; + if (!dq) return 0; + free(dq->data); + free(dq); + return 0; +} + +/* Filesystem Operations */ +#include +#include + +long long fs_scan_dir(long long path_val) { + const char* path = (const char*)path_val; + long long list_ptr = create_list(); + if (!path) return list_ptr; + DIR* d = opendir(path); + if (!d) return list_ptr; + struct dirent* dir; + while ((dir = readdir(d)) != NULL) { + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { + continue; + } + char* name = strdup(dir->d_name); + append_list(list_ptr, (long long)name); + } + closedir(d); + return list_ptr; +} + +long long fs_copy_file(long long src_val, long long dest_val) { + const char* src = (const char*)src_val; + const char* dest = (const char*)dest_val; + if (!src || !dest) return 0; + FILE* f_src = fopen(src, "rb"); + if (!f_src) return 0; + FILE* f_dest = fopen(dest, "wb"); + if (!f_dest) { + fclose(f_src); + return 0; + } + char buf[4096]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) { + fwrite(buf, 1, n, f_dest); + } + fclose(f_src); + fclose(f_dest); + return 1; +} + +long long fs_delete_file(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + return remove(path) == 0 ? 1 : 0; +} + +long long fs_move_file(long long src_val, long long dest_val) { + const char* src = (const char*)src_val; + const char* dest = (const char*)dest_val; + if (!src || !dest) return 0; + return rename(src, dest) == 0 ? 1 : 0; +} + +long long fs_exists(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + return stat(path, &st) == 0 ? 1 : 0; +} + +long long fs_is_dir(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + if (stat(path, &st) != 0) return 0; + return S_ISDIR(st.st_mode) ? 1 : 0; +} + +long long fs_is_file(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + if (stat(path, &st) != 0) return 0; + return S_ISREG(st.st_mode) ? 1 : 0; +} + +long long fs_get_size(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + struct stat st; + if (stat(path, &st) != 0) return 0; + return (long long)st.st_size; +} + +/* HTTP Client */ +#ifdef __wasm__ +long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) { + (void)method_val; (void)url_val; (void)headers_val; (void)body_val; + return (long long)strdup("Error: HTTP request is not supported on WebAssembly"); +} +#else +long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) { + const char* method = (const char*)method_val; + const char* url = (const char*)url_val; + const char* headers = (const char*)headers_val; + const char* body = (const char*)body_val; + if (!method || !url) return (long long)strdup(""); + if (strncmp(url, "http://", 7) != 0) { + return (long long)strdup("Error: only http:// protocol supported"); + } + const char* host_start = url + 7; + const char* path_start = strchr(host_start, '/'); + char host[256]; + char path[1024]; + if (path_start) { + size_t host_len = path_start - host_start; + if (host_len >= sizeof(host)) host_len = sizeof(host) - 1; + strncpy(host, host_start, host_len); + host[host_len] = '\0'; + strncpy(path, path_start, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + } else { + strncpy(host, host_start, sizeof(host) - 1); + host[sizeof(host) - 1] = '\0'; + strcpy(path, "/"); + } + int port = 80; + char* colon = strchr(host, ':'); + if (colon) { + *colon = '\0'; + port = atoi(colon + 1); + } + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) return (long long)strdup("Error: socket creation failed"); + struct hostent* server = gethostbyname(host); + if (!server) { + close(sockfd); + return (long long)strdup("Error: host resolution failed"); + } + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length); + serv_addr.sin_port = htons(port); + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + close(sockfd); + return (long long)strdup("Error: connection failed"); + } + char req[4096]; + size_t body_len = body ? strlen(body) : 0; + int req_len = snprintf(req, sizeof(req), + "%s %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Content-Length: %zu\r\n" + "Connection: close\r\n" + "%s%s" + "\r\n", + method, path, host, body_len, headers ? headers : "", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\n') ? "\r\n" : ""); + if (send(sockfd, req, req_len, 0) < 0) { + close(sockfd); + return (long long)strdup("Error: send failed"); + } + if (body_len > 0) { + if (send(sockfd, body, body_len, 0) < 0) { + close(sockfd); + return (long long)strdup("Error: send body failed"); + } + } + size_t resp_cap = 4096; + size_t resp_len = 0; + char* resp = malloc(resp_cap); + if (!resp) { + close(sockfd); + return (long long)strdup(""); + } + char recv_buf[4096]; + ssize_t n; + while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) { + if (resp_len + n >= resp_cap) { + resp_cap *= 2; + char* new_resp = realloc(resp, resp_cap); + if (!new_resp) { + free(resp); + close(sockfd); + return (long long)strdup("Error: memory allocation failed"); + } + resp = new_resp; + } + memcpy(resp + resp_len, recv_buf, n); + resp_len += n; + } + resp[resp_len] = '\0'; + close(sockfd); + // Strip HTTP headers — return only the body after \r\n\r\n + char* http_body = strstr(resp, "\r\n\r\n"); + if (http_body) { + http_body += 4; + char* result = strdup(http_body); + free(resp); + return (long long)result; + } + return (long long)resp; +} +#endif + +#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits)))) +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +typedef struct { + unsigned char data[64]; + unsigned int datalen; + unsigned long long bitlen; + unsigned int state[8]; +} EP_SHA256_CTX; + +static const unsigned int sha256_k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) { + unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; + e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; + } + ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; + ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; +} + +void ep_sha256_init(EP_SHA256_CTX *ctx) { + ctx->datalen = 0; ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; +} + +void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) { + for (size_t i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + ep_sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) { + unsigned int i = ctx->datalen; + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) ctx->data[i++] = 0x00; + } else { + ctx->data[i++] = 0x80; + while (i < 64) ctx->data[i++] = 0x00; + ep_sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; + ep_sha256_transform(ctx, ctx->data); + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} + +char* ep_sha256(const char* s) { + if (!s) s = ""; + EP_SHA256_CTX ctx; + ep_sha256_init(&ctx); + ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s)); + unsigned char hash[32]; + ep_sha256_final(&ctx, hash); + char* result = malloc(65); + if (result) { + for (int i = 0; i < 32; i++) { + snprintf(result + (i * 2), 3, "%02x", hash[i]); + } + result[64] = '\0'; + } + return result; +} + +/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary + safe), so keys/messages containing NUL bytes hash correctly. Returns a + malloc'd 64-char lowercase hex string. */ +long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) { + const unsigned char* key = (const unsigned char*)key_ptr; + const unsigned char* msg = (const unsigned char*)msg_ptr; + size_t klen = (size_t)key_len; + size_t mlen = (size_t)msg_len; + + unsigned char k0[64]; + memset(k0, 0, sizeof(k0)); + if (klen > 64) { + /* Keys longer than the block size are replaced by their hash. */ + EP_SHA256_CTX kc; + ep_sha256_init(&kc); + ep_sha256_update(&kc, key ? key : (const unsigned char*)"", klen); + unsigned char kh[32]; + ep_sha256_final(&kc, kh); + memcpy(k0, kh, 32); + } else if (key) { + memcpy(k0, key, klen); + } + + unsigned char ipad[64], opad[64]; + for (int i = 0; i < 64; i++) { + ipad[i] = k0[i] ^ 0x36; + opad[i] = k0[i] ^ 0x5c; + } + + /* inner = H((K0 ^ ipad) || message) */ + EP_SHA256_CTX ic; + ep_sha256_init(&ic); + ep_sha256_update(&ic, ipad, 64); + if (msg && mlen) ep_sha256_update(&ic, msg, mlen); + unsigned char inner[32]; + ep_sha256_final(&ic, inner); + + /* mac = H((K0 ^ opad) || inner) */ + EP_SHA256_CTX oc; + ep_sha256_init(&oc); + ep_sha256_update(&oc, opad, 64); + ep_sha256_update(&oc, inner, 32); + unsigned char mac[32]; + ep_sha256_final(&oc, mac); + + char* out = (char*)malloc(65); + if (out) { + for (int i = 0; i < 32; i++) { + snprintf(out + (i * 2), 3, "%02x", mac[i]); + } + out[64] = '\0'; + } + return (long long)out; +} + +typedef struct { + unsigned int count[2]; + unsigned int state[4]; + unsigned char buffer[64]; +} EP_MD5_CTX; + +#define F(x,y,z) (((x) & (y)) | (~(x) & (z))) +#define G(x,y,z) (((x) & (z)) | ((y) & ~(z))) +#define H(x,y,z) ((x) ^ (y) ^ (z)) +#define I(x,y,z) ((y) ^ ((x) | ~(z))) +#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n)))) + +#define FF(a,b,c,d,x,s,ac) { \ + (a) += F((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} +#define GG(a,b,c,d,x,s,ac) { \ + (a) += G((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} +#define HH(a,b,c,d,x,s,ac) { \ + (a) += H((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} +#define II(a,b,c,d,x,s,ac) { \ + (a) += I((b),(c),(d)) + (x) + (ac); \ + (a) = ROTATE_LEFT((a),(s)); \ + (a) += (b); \ +} + +void ep_md5_init(EP_MD5_CTX *ctx) { + ctx->count[0] = ctx->count[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) { + unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + for (int i = 0, j = 0; i < 16; i++, j += 4) + x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24); + + FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee); + FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501); + FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be); + FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821); + + GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); + GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); + GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed); + GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); + + HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c); + HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70); + HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05); + HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665); + + II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039); + II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1); + II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1); + II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391); + + state[0] += a; state[1] += b; state[2] += c; state[3] += d; +} + +void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) { + unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index; + ctx->count[0] += input_len << 3; + if (ctx->count[0] < (input_len << 3)) ctx->count[1]++; + ctx->count[1] += input_len >> 29; + if (input_len >= part_len) { + memcpy(&ctx->buffer[index], input, part_len); + ep_md5_transform(ctx->state, ctx->buffer); + for (i = part_len; i + 63 < input_len; i += 64) + ep_md5_transform(ctx->state, &input[i]); + index = 0; + } + memcpy(&ctx->buffer[index], &input[i], input_len - i); +} + +void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) { + unsigned char bits[8]; + bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24; + bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24; + unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index); + unsigned char padding[64]; + memset(padding, 0, 64); padding[0] = 0x80; + ep_md5_update(ctx, padding, pad_len); + ep_md5_update(ctx, bits, 8); + for (int i = 0; i < 4; i++) { + digest[i*4] = ctx->state[i]; + digest[i*4 + 1] = ctx->state[i] >> 8; + digest[i*4 + 2] = ctx->state[i] >> 16; + digest[i*4 + 3] = ctx->state[i] >> 24; + } +} + +char* ep_md5(const char* s) { + if (!s) s = ""; + EP_MD5_CTX ctx; + ep_md5_init(&ctx); + ep_md5_update(&ctx, (const unsigned char*)s, strlen(s)); + unsigned char hash[16]; + ep_md5_final(&ctx, hash); + char* result = malloc(33); + if (result) { + for (int i = 0; i < 16; i++) { + snprintf(result + (i * 2), 3, "%02x", hash[i]); + } + result[32] = '\0'; + } + return result; +} + +char* read_file_content(const char* filepath) { + char mode[3]; + mode[0] = 'r'; + mode[1] = 'b'; + mode[2] = '\0'; + FILE* f = fopen(filepath, mode); + if (!f) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = malloc(size + 1); + if (!buf) { + fclose(f); + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + size_t read_bytes = fread(buf, 1, size, f); + buf[read_bytes] = '\0'; + fclose(f); + ep_gc_register(buf, EP_OBJ_STRING); + return buf; +} + +long long string_length(const char* s) { + if (!s) return 0; + return strlen(s); +} + +long long get_character(const char* s, long long index) { + if (!s) return 0; + long long len = strlen(s); + if (index < 0 || index >= len) return 0; + return (unsigned char)s[index]; +} + +long long create_list(void) { + EpList* list = malloc(sizeof(EpList)); + if (!list) return 0; + list->capacity = 4; + list->length = 0; + list->data = malloc(list->capacity * sizeof(long long)); + ep_gc_register(list, EP_OBJ_LIST); + return (long long)list; +} + +long long get_list_data_ptr(long long list_ptr) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list) return 0; + return (long long)list->data; +} + +long long append_list(long long list_ptr, long long value) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list) return 0; + if (list->length >= list->capacity) { + list->capacity *= 2; + list->data = realloc(list->data, list->capacity * sizeof(long long)); + } + list->data[list->length] = value; + list->length += 1; + ep_gc_write_barrier((void*)list_ptr, value); + return value; +} + +long long get_list(long long list_ptr, long long index) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (index < 0 || index >= list->length) return 0; + return list->data[index]; +} + +long long set_list(long long list_ptr, long long index, long long value) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (index < 0 || index >= list->length) return 0; + list->data[index] = value; + ep_gc_write_barrier((void*)list_ptr, value); + return value; +} + +long long length_list(long long list_ptr) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + return list->length; +} + +long long free_list(long long list_ptr) { + EpList* list = (EpList*)list_ptr; + if (!list) return 0; + /* Skip if already freed (idempotent) */ + if (!ep_gc_find(list)) return 0; + ep_gc_unregister(list); + free(list->data); + free(list); + return 0; +} + +static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) { + EpList* rows = (EpList*)arg; + EpList* row = (EpList*)create_list(); + for (int i = 0; i < argc; i++) { + char* val = argv[i] ? strdup(argv[i]) : strdup(""); + append_list((long long)row, (long long)val); + } + append_list((long long)rows, (long long)row); + return 0; +} + +long long sqlite_get_callback_ptr(long long dummy) { + return (long long)sqlite_list_callback; +} + +/* SQLite type-safe wrappers — marshal between int and long long */ +#ifdef EP_HAS_SQLITE +typedef struct sqlite3 sqlite3; +int sqlite3_open(const char*, sqlite3**); +int sqlite3_close(sqlite3*); +int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**); + +long long ep_sqlite3_open(long long filename, long long db_ptr) { + sqlite3* db = NULL; + int rc = sqlite3_open((const char*)filename, &db); + if (rc == 0 && db_ptr != 0) { + *((long long*)db_ptr) = (long long)db; + } + return (long long)rc; +} + +long long ep_sqlite3_close(long long db) { + return (long long)sqlite3_close((sqlite3*)db); +} + +long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) { + return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql, + (int(*)(void*,int,char**,char**))(callback), + (void*)cb_arg, (char**)errmsg_ptr); +} + +/* Prepared-statement API for parameterized queries (defeats SQL injection). */ +typedef struct sqlite3_stmt sqlite3_stmt; +int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**); +int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*)); +int sqlite3_bind_int64(sqlite3_stmt*, int, long long); +int sqlite3_step(sqlite3_stmt*); +int sqlite3_column_count(sqlite3_stmt*); +const unsigned char* sqlite3_column_text(sqlite3_stmt*, int); +long long sqlite3_column_int64(sqlite3_stmt*, int); +int sqlite3_finalize(sqlite3_stmt*); + +long long ep_sqlite3_prepare_v2(long long db, long long sql) { + sqlite3_stmt* stmt = NULL; + int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL); + if (rc != 0) return 0; + return (long long)stmt; +} + +long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) { + /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is + a bound parameter, never concatenated into SQL — this is the safe path. */ + return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx, + (const char*)value, -1, (void(*)(void*))(intptr_t)-1); +} + +long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) { + return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value); +} + +long long ep_sqlite3_step(long long stmt) { + return (long long)sqlite3_step((sqlite3_stmt*)stmt); +} + +long long ep_sqlite3_column_count(long long stmt) { + return (long long)sqlite3_column_count((sqlite3_stmt*)stmt); +} + +long long ep_sqlite3_column_text(long long stmt, long long col) { + const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col); + if (!t) return (long long)strdup(""); + return (long long)strdup((const char*)t); +} + +long long ep_sqlite3_column_int(long long stmt, long long col) { + return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col); +} + +long long ep_sqlite3_finalize(long long stmt) { + return (long long)sqlite3_finalize((sqlite3_stmt*)stmt); +} +#endif /* EP_HAS_SQLITE */ + +int ep_argc = 0; +char** ep_argv = NULL; + +void init_ep_args(int argc, char** argv) { + ep_argc = argc; + ep_argv = argv; + ep_gc_register_thread((void*)&argc); + /* Wire up channel scanning for GC (defined after EpChannel struct) */ + ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl; + ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl; + /* Wire up map value traversal for GC (defined after EpMap struct) */ + ep_gc_mark_map_values = ep_gc_mark_map_values_impl; + ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl; +} + +long long get_argument_count(void) { + return ep_argc; +} + +const char* get_argument(long long index) { + if (index < 0 || index >= ep_argc) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + return empty; + } + return ep_argv[index]; +} + +long long write_file_content(const char* filepath, const char* content) { + char mode[3]; + mode[0] = 'w'; + mode[1] = 'b'; + mode[2] = '\0'; + FILE* f = fopen(filepath, mode); + if (!f) return 0; + size_t len = strlen(content); + size_t written = fwrite(content, 1, len, f); + fclose(f); + return written == len ? 1 : 0; +} + +long long run_command(const char* command) { + if (!command) return -1; + return system(command); +} + +char* substring(const char* s, long long start, long long len) { + if (!s) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + long long total_len = strlen(s); + if (start < 0 || start >= total_len || len <= 0) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + if (start + len > total_len) { + len = total_len - start; + } + char* sub = malloc(len + 1); + if (!sub) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + strncpy(sub, s + start, len); + sub[len] = '\0'; + ep_gc_register(sub, EP_OBJ_STRING); + return sub; +} + +char* string_from_list(long long list_ptr) { + EpList* list = (EpList*)list_ptr; + if (!list) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + char* s = malloc(list->length + 1); + if (!s) { + char* empty = malloc(1); + if (empty) empty[0] = '\0'; + ep_gc_register(empty, EP_OBJ_STRING); + return empty; + } + for (long long i = 0; i < list->length; i++) { + s[i] = (char)list->data[i]; + } + s[list->length] = '\0'; + ep_gc_register(s, EP_OBJ_STRING); + return s; +} + +// Inverse of string_from_list: convert a string to a list of its byte values in +// a single O(n) pass (one strlen + one copy). This lets callers iterate a string +// in O(n) total via O(1) get_list, instead of O(n) get_character per index +// (which is O(n^2) over the whole string). +long long string_to_list(const char* s) { + EpList* list = malloc(sizeof(EpList)); + if (!list) return 0; + long long len = s ? (long long)strlen(s) : 0; + list->capacity = len > 0 ? len : 4; + list->length = len; + list->data = malloc(list->capacity * sizeof(long long)); + if (!list->data) { + list->capacity = 0; + list->length = 0; + ep_gc_register(list, EP_OBJ_LIST); + return (long long)list; + } + for (long long i = 0; i < len; i++) { + list->data[i] = (unsigned char)s[i]; + } + ep_gc_register(list, EP_OBJ_LIST); + return (long long)list; +} + +long long pop_list(long long list_ptr) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list || list->length <= 0) return 0; + list->length -= 1; + return list->data[list->length]; +} + +long long remove_list(long long list_ptr, long long index) { + if (EP_BADPTR(list_ptr)) return 0; + EpList* list = (EpList*)list_ptr; + if (!list || index < 0 || index >= list->length) return 0; + long long removed = list->data[index]; + for (long long i = index; i < list->length - 1; i++) { + list->data[i] = list->data[i + 1]; + } + list->length -= 1; + return removed; +} + +long long display_string(const char* s) { + if (s) puts(s); + return 0; +} + +/* ========== File System Runtime ========== */ +#include +#ifdef _WIN32 + #include + #include + #define mkdir(p, m) _mkdir(p) + #define rmdir _rmdir + #define getcwd _getcwd + #define popen _popen + #define pclose _pclose + #define getpid _getpid + #define setenv(k, v, o) _putenv_s(k, v) + /* Minimal dirent polyfill for Windows */ + #include + typedef struct { char d_name[260]; } ep_dirent; + typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR; + static EP_DIR* ep_opendir(const char* p) { + EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR)); + char buf[270]; snprintf(buf, sizeof(buf), "%s\\*", p); + d->hFind = FindFirstFileA(buf, &d->data); + d->first = 1; + return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d; + } + static ep_dirent* ep_readdir(EP_DIR* d) { + static ep_dirent ent; + if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; } + if (!FindNextFileA(d->hFind, &d->data)) return NULL; + strcpy(ent.d_name, d->data.cFileName); return &ent; + } + static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); } + #define DIR EP_DIR + #define dirent ep_dirent + #define opendir ep_opendir + #define readdir ep_readdir + #define closedir ep_closedir +#else + #include + #include +#endif + +long long ep_read_file(long long path_ptr) { + const char* path = (const char*)path_ptr; + FILE* f = fopen(path, "rb"); + if (!f) return (long long)""; + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = (char*)malloc(size + 1); + fread(buf, 1, size, f); + buf[size] = '\0'; + fclose(f); + return (long long)buf; +} + +long long ep_write_file(long long path_ptr, long long content_ptr) { + const char* path = (const char*)path_ptr; + const char* content = (const char*)content_ptr; + FILE* f = fopen(path, "wb"); + if (!f) return 0; + fputs(content, f); + fclose(f); + return 1; +} + +long long ep_append_file(long long path_ptr, long long content_ptr) { + const char* path = (const char*)path_ptr; + const char* content = (const char*)content_ptr; + FILE* f = fopen(path, "ab"); + if (!f) return 0; + fputs(content, f); + fclose(f); + return 1; +} + +long long ep_file_exists(long long path_ptr) { + const char* path = (const char*)path_ptr; + struct stat st; + return stat(path, &st) == 0 ? 1 : 0; +} + +long long ep_is_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + struct stat st; + if (stat(path, &st) != 0) return 0; + return S_ISDIR(st.st_mode) ? 1 : 0; +} + +long long ep_file_size(long long path_ptr) { + const char* path = (const char*)path_ptr; + struct stat st; + if (stat(path, &st) != 0) return -1; + return (long long)st.st_size; +} + +long long ep_list_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + DIR* dir = opendir(path); + if (!dir) return (long long)create_list(); + long long list = create_list(); + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || + (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) continue; + char* name = strdup(entry->d_name); + append_list(list, (long long)name); + } + closedir(dir); + return list; +} + +long long ep_create_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + return mkdir(path, 0755) == 0 ? 1 : 0; +} + +long long ep_remove_file(long long path_ptr) { + const char* path = (const char*)path_ptr; + return remove(path) == 0 ? 1 : 0; +} + +long long ep_remove_directory(long long path_ptr) { + const char* path = (const char*)path_ptr; + return rmdir(path) == 0 ? 1 : 0; +} + +long long ep_rename_file(long long old_ptr, long long new_ptr) { + return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0; +} + +long long ep_copy_file(long long src_ptr, long long dst_ptr) { + const char* src = (const char*)src_ptr; + const char* dst = (const char*)dst_ptr; + FILE* fin = fopen(src, "rb"); + if (!fin) return 0; + FILE* fout = fopen(dst, "wb"); + if (!fout) { fclose(fin); return 0; } + char buf[8192]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) { + fwrite(buf, 1, n, fout); + } + fclose(fin); + fclose(fout); + return 1; +} + +/* ========== Date/Time Runtime ========== */ +#include +#include + +long long ep_time_now_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL; +} + +long long ep_time_now_sec(void) { + return (long long)time(NULL); +} + + +long long ep_time_year(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_year + 1900 : 0; +} + +long long ep_time_month(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_mon + 1 : 0; +} + +long long ep_time_day(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_mday : 0; +} + +long long ep_time_hour(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_hour : 0; +} + +long long ep_time_minute(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_min : 0; +} + +long long ep_time_second(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_sec : 0; +} + +long long ep_time_weekday(long long ts) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + return tm ? tm->tm_wday : 0; +} + +long long ep_format_time(long long ts, long long fmt_ptr) { + time_t t = (time_t)ts; + struct tm* tm = localtime(&t); + if (!tm) return (long long)""; + char* buf = (char*)malloc(256); + strftime(buf, 256, (const char*)fmt_ptr, tm); + return (long long)buf; +} + +/* ========== OS Runtime ========== */ + +long long ep_getenv(long long name_ptr) { + const char* val = getenv((const char*)name_ptr); + return val ? (long long)val : (long long)""; +} + +long long ep_setenv(long long name_ptr, long long val_ptr) { + return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0; +} + +long long ep_get_cwd(void) { + char* buf = (char*)malloc(4096); + if (getcwd(buf, 4096)) return (long long)buf; + free(buf); + return (long long)""; +} + +long long ep_os_name(void) { + #if defined(__APPLE__) + return (long long)"macos"; + #elif defined(__linux__) + return (long long)"linux"; + #elif defined(_WIN32) + return (long long)"windows"; + #else + return (long long)"unknown"; + #endif +} + +long long ep_arch_name(void) { + #if defined(__aarch64__) || defined(__arm64__) + return (long long)"arm64"; + #elif defined(__x86_64__) + return (long long)"x86_64"; + #elif defined(__i386__) + return (long long)"x86"; + #else + return (long long)"unknown"; + #endif +} + +long long ep_exit(long long code) { + exit((int)code); + return 0; +} + +long long ep_get_pid(void) { + return (long long)getpid(); +} + +long long ep_get_home_dir(void) { + const char* home = getenv("HOME"); + return home ? (long long)home : (long long)""; +} + +#ifdef __wasm__ +long long ep_run_command(long long cmd_ptr) { + (void)cmd_ptr; + return (long long)"Error: running external commands is not supported on WebAssembly"; +} +#else +long long ep_run_command(long long cmd_ptr) { + const char* cmd = (const char*)cmd_ptr; + FILE* fp = popen(cmd, "r"); + if (!fp) return (long long)""; + char* result = (char*)malloc(65536); + size_t total = 0; + char buf[4096]; + while (fgets(buf, sizeof(buf), fp)) { + size_t len = strlen(buf); + memcpy(result + total, buf, len); + total += len; + } + result[total] = '\0'; + pclose(fp); + return (long long)result; +} +#endif + +/* ========== HashMap helpers ========== */ + +long long ep_hash_string(long long s_ptr) { + const char* s = (const char*)s_ptr; + if (!s) return 0; + unsigned long long hash = 5381; + int c; + while ((c = *s++)) { + hash = ((hash << 5) + hash) + c; + } + return (long long)hash; +} + +long long ep_str_equals(long long a_ptr, long long b_ptr) { + if (a_ptr == b_ptr) return 1; + if (!a_ptr || !b_ptr) return 0; + /* If either value looks like a small integer (not a valid heap pointer), + fall back to integer comparison — strcmp would segfault. */ + if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0; + return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0; +} + +/* ========== Sync Primitives ========== */ + +#ifdef _WIN32 +long long ep_mutex_create(void) { + CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection(m); + return (long long)m; +} +long long ep_mutex_lock_fn(long long m) { + EnterCriticalSection((CRITICAL_SECTION*)m); + return 1; +} +long long ep_mutex_unlock_fn(long long m) { + LeaveCriticalSection((CRITICAL_SECTION*)m); + return 1; +} +long long ep_mutex_trylock(long long m) { + return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0; +} +long long ep_mutex_destroy(long long m) { + DeleteCriticalSection((CRITICAL_SECTION*)m); + free((void*)m); + return 0; +} +#else +long long ep_mutex_create(void) { + pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m, NULL); + return (long long)m; +} + +long long ep_mutex_lock_fn(long long m) { + return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_mutex_unlock_fn(long long m) { + return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_mutex_trylock(long long m) { + return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_mutex_destroy(long long m) { + pthread_mutex_destroy((pthread_mutex_t*)m); + free((void*)m); + return 0; +} +#endif + +#ifdef _WIN32 +long long ep_rwlock_create(void) { + SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK)); + InitializeSRWLock(rwl); + return (long long)rwl; +} +long long ep_rwlock_read_lock(long long rwl) { + AcquireSRWLockShared((SRWLOCK*)rwl); + return 1; +} +long long ep_rwlock_write_lock(long long rwl) { + AcquireSRWLockExclusive((SRWLOCK*)rwl); + return 1; +} +long long ep_rwlock_unlock(long long rwl) { + /* SRWLOCK does not have a single "unlock" — we try exclusive first. + In practice the caller should know which lock was taken. + ReleaseSRWLockExclusive on a shared lock is undefined, but + the runtime guarantees matched lock/unlock pairs. We default + to releasing the exclusive lock; shared unlock is handled + by pairing read_lock -> read_unlock if needed later. */ + ReleaseSRWLockExclusive((SRWLOCK*)rwl); + return 1; +} +long long ep_rwlock_destroy(long long rwl) { + /* SRWLOCK has no destroy */ + free((void*)rwl); + return 0; +} +#else +long long ep_rwlock_create(void) { + pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t)); + pthread_rwlock_init(rwl, NULL); + return (long long)rwl; +} + +long long ep_rwlock_read_lock(long long rwl) { + return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; +} + +long long ep_rwlock_write_lock(long long rwl) { + return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; +} + +long long ep_rwlock_unlock(long long rwl) { + return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0; +} + +long long ep_rwlock_destroy(long long rwl) { + pthread_rwlock_destroy((pthread_rwlock_t*)rwl); + free((void*)rwl); + return 0; +} +#endif + +#ifdef _MSC_VER +long long ep_atomic_create(long long initial) { + volatile long long* a = (volatile long long*)malloc(sizeof(long long)); + InterlockedExchange64(a, initial); + return (long long)a; +} +long long ep_atomic_load(long long a) { + return InterlockedCompareExchange64((volatile long long*)a, 0, 0); +} +long long ep_atomic_store(long long a, long long value) { + InterlockedExchange64((volatile long long*)a, value); + return value; +} +long long ep_atomic_add(long long a, long long delta) { + return InterlockedExchangeAdd64((volatile long long*)a, delta); +} +long long ep_atomic_sub(long long a, long long delta) { + return InterlockedExchangeAdd64((volatile long long*)a, -delta); +} +long long ep_atomic_cas(long long a, long long expected, long long desired) { + long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected); + return (old == expected) ? 1 : 0; +} +#else +long long ep_atomic_create(long long initial) { + long long* a = (long long*)malloc(sizeof(long long)); + __atomic_store_n(a, initial, __ATOMIC_SEQ_CST); + return (long long)a; +} + +long long ep_atomic_load(long long a) { + return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST); +} + +long long ep_atomic_store(long long a, long long value) { + __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST); + return value; +} + +long long ep_atomic_add(long long a, long long delta) { + return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST); +} + +long long ep_atomic_sub(long long a, long long delta) { + return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST); +} + +long long ep_atomic_cas(long long a, long long expected, long long desired) { + long long exp = expected; + return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0; +} +#endif + +/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */ +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + unsigned count; + unsigned target; + unsigned generation; +} EpBarrier; + +long long ep_barrier_create(long long count) { + EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier)); + pthread_mutex_init(&b->mutex, NULL); + pthread_cond_init(&b->cond, NULL); + b->count = 0; + b->target = (unsigned)count; + b->generation = 0; + return (long long)b; +} + +long long ep_barrier_wait(long long bp) { + EpBarrier* b = (EpBarrier*)bp; + pthread_mutex_lock(&b->mutex); + unsigned gen = b->generation; + b->count++; + if (b->count >= b->target) { + b->count = 0; + b->generation++; + pthread_cond_broadcast(&b->cond); + pthread_mutex_unlock(&b->mutex); + return 1; /* serial thread */ + } + while (gen == b->generation) { + pthread_cond_wait(&b->cond, &b->mutex); + } + pthread_mutex_unlock(&b->mutex); + return 0; +} + +long long ep_barrier_destroy(long long bp) { + EpBarrier* b = (EpBarrier*)bp; + pthread_mutex_destroy(&b->mutex); + pthread_cond_destroy(&b->cond); + free(b); + return 0; +} + +/* Semaphore via mutex+condvar (portable) */ +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + long long value; +} EpSemaphore; + +long long ep_semaphore_create(long long initial) { + EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore)); + pthread_mutex_init(&s->mutex, NULL); + pthread_cond_init(&s->cond, NULL); + s->value = initial; + return (long long)s; +} + +long long ep_semaphore_wait(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_lock(&s->mutex); + while (s->value <= 0) { + pthread_cond_wait(&s->cond, &s->mutex); + } + s->value--; + pthread_mutex_unlock(&s->mutex); + return 1; +} + +long long ep_semaphore_post(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_lock(&s->mutex); + s->value++; + pthread_cond_signal(&s->cond); + pthread_mutex_unlock(&s->mutex); + return 1; +} + +long long ep_semaphore_trywait(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_lock(&s->mutex); + if (s->value > 0) { + s->value--; + pthread_mutex_unlock(&s->mutex); + return 1; + } + pthread_mutex_unlock(&s->mutex); + return 0; +} + +long long ep_semaphore_destroy(long long sp) { + EpSemaphore* s = (EpSemaphore*)sp; + pthread_mutex_destroy(&s->mutex); + pthread_cond_destroy(&s->cond); + free(s); + return 0; +} + +long long ep_condvar_create(void) { + pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t)); + pthread_cond_init(cv, NULL); + return (long long)cv; +} + +long long ep_condvar_wait(long long cv, long long m) { + return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0; +} + +long long ep_condvar_signal(long long cv) { + return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0; +} + +long long ep_condvar_broadcast(long long cv) { + return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0; +} + +long long ep_condvar_destroy(long long cv) { + pthread_cond_destroy((pthread_cond_t*)cv); + free((void*)cv); + return 0; +} + +/* ========== Regex (simple stub — delegates to POSIX regex) ========== */ +#include + +long long ep_regex_match(long long pattern_ptr, long long text_ptr) { + regex_t regex; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB); + if (ret) return 0; + ret = regexec(®ex, text, 0, NULL, 0); + regfree(®ex); + return ret == 0 ? 1 : 0; +} + +long long ep_regex_find(long long pattern_ptr, long long text_ptr) { + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) return (long long)""; + ret = regexec(®ex, text, 1, &match, 0); + if (ret != 0) { regfree(®ex); return (long long)""; } + int len = match.rm_eo - match.rm_so; + char* result = (char*)malloc(len + 1); + memcpy(result, text + match.rm_so, len); + result[len] = '\0'; + regfree(®ex); + return (long long)result; +} + +long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) { + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + long long list = create_list(); + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) return list; + const char* cursor = text; + while (regexec(®ex, cursor, 1, &match, 0) == 0) { + int len = match.rm_eo - match.rm_so; + char* result = (char*)malloc(len + 1); + memcpy(result, cursor + match.rm_so, len); + result[len] = '\0'; + append_list(list, (long long)result); + cursor += match.rm_eo; + if (match.rm_eo == 0) break; + } + regfree(®ex); + return list; +} + +long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) { + /* Simple single-replacement via regex */ + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + const char* repl = (const char*)repl_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) return text_ptr; + ret = regexec(®ex, text, 1, &match, 0); + if (ret != 0) { regfree(®ex); return text_ptr; } + size_t tlen = strlen(text); + size_t rlen = strlen(repl); + size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen; + char* result = (char*)malloc(new_len + 1); + memcpy(result, text, match.rm_so); + memcpy(result + match.rm_so, repl, rlen); + memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo); + result[new_len] = '\0'; + regfree(®ex); + return (long long)result; +} + +long long ep_regex_split(long long pattern_ptr, long long text_ptr) { + long long list = create_list(); + /* Simple split: find matches and split around them */ + regex_t regex; + regmatch_t match; + const char* pattern = (const char*)pattern_ptr; + const char* text = (const char*)text_ptr; + int ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret) { + append_list(list, text_ptr); + return list; + } + const char* cursor = text; + while (regexec(®ex, cursor, 1, &match, 0) == 0) { + int len = match.rm_so; + char* part = (char*)malloc(len + 1); + memcpy(part, cursor, len); + part[len] = '\0'; + append_list(list, (long long)part); + cursor += match.rm_eo; + if (match.rm_eo == 0) break; + } + char* rest = strdup(cursor); + append_list(list, (long long)rest); + regfree(®ex); + return list; +} + +/* ========== Base64 ========== */ +static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +long long ep_base64_encode(long long data_ptr) { + const unsigned char* data = (const unsigned char*)data_ptr; + size_t len = strlen((const char*)data); + size_t out_len = 4 * ((len + 2) / 3); + char* out = (char*)malloc(out_len + 1); + size_t i, j = 0; + for (i = 0; i < len; i += 3) { + unsigned int n = data[i] << 16; + if (i + 1 < len) n |= data[i+1] << 8; + if (i + 2 < len) n |= data[i+2]; + out[j++] = b64_table[(n >> 18) & 63]; + out[j++] = b64_table[(n >> 12) & 63]; + out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '='; + out[j++] = (i + 2 < len) ? b64_table[n & 63] : '='; + } + out[j] = '\0'; + return (long long)out; +} + +long long ep_uuid_v4(void) { + char* uuid = (char*)malloc(37); + unsigned char bytes[16]; + ep_secure_random_bytes(bytes, 16); + bytes[6] = (bytes[6] & 0x0F) | 0x40; + bytes[8] = (bytes[8] & 0x3F) | 0x80; + snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], + bytes[12], bytes[13], bytes[14], bytes[15]); + return (long long)uuid; +} + +long long file_read(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return (long long)strdup(""); + FILE* f = fopen(path, "rb"); + if (!f) return (long long)strdup(""); + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = malloc(size + 1); + if (!buf) { fclose(f); return (long long)strdup(""); } + fread(buf, 1, size, f); + buf[size] = '\0'; + fclose(f); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + +long long file_write(long long path_val, long long content_val) { + const char* path = (const char*)path_val; + const char* content = (const char*)content_val; + if (!path || !content) return 0; + FILE* f = fopen(path, "wb"); + if (!f) return 0; + size_t len = strlen(content); + fwrite(content, 1, len, f); + fclose(f); + return 1; +} + +long long file_append(long long path_val, long long content_val) { + const char* path = (const char*)path_val; + const char* content = (const char*)content_val; + if (!path || !content) return 0; + FILE* f = fopen(path, "ab"); + if (!f) return 0; + size_t len = strlen(content); + fwrite(content, 1, len, f); + fclose(f); + return 1; +} + +long long file_exists(long long path_val) { + const char* path = (const char*)path_val; + if (!path) return 0; + FILE* f = fopen(path, "r"); + if (f) { fclose(f); return 1; } + return 0; +} + +long long string_contains(long long s_val, long long sub_val) { + const char* s = (const char*)s_val; + const char* sub = (const char*)sub_val; + if (!s || !sub) return 0; + return strstr(s, sub) != NULL ? 1 : 0; +} + +long long string_index_of(long long s_val, long long sub_val) { + const char* s = (const char*)s_val; + const char* sub = (const char*)sub_val; + if (!s || !sub) return -1; + const char* found = strstr(s, sub); + if (!found) return -1; + return (long long)(found - s); +} + +long long string_replace(long long s_val, long long old_val, long long new_val) { + const char* s = (const char*)s_val; + const char* old_str = (const char*)old_val; + const char* new_str = (const char*)new_val; + if (!s || !old_str || !new_str) return (long long)strdup(s ? s : ""); + size_t old_len = strlen(old_str); + size_t new_len = strlen(new_str); + if (old_len == 0) return (long long)strdup(s); + int count = 0; + const char* p = s; + while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; } + size_t result_len = strlen(s) + count * (new_len - old_len); + char* result = malloc(result_len + 1); + if (!result) return (long long)strdup(s); + char* dst = result; + p = s; + while (*p) { + if (strncmp(p, old_str, old_len) == 0) { + memcpy(dst, new_str, new_len); + dst += new_len; + p += old_len; + } else { + *dst++ = *p++; + } + } + *dst = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +/* ========== Additional String Functions ========== */ +#include + +long long string_upper(long long s_val) { + const char* s = (const char*)s_val; + if (!s) return (long long)strdup(""); + long long len = strlen(s); + char* result = malloc(len + 1); + for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]); + result[len] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long string_lower(long long s_val) { + const char* s = (const char*)s_val; + if (!s) return (long long)strdup(""); + long long len = strlen(s); + char* result = malloc(len + 1); + for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]); + result[len] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long string_trim(long long s_val) { + const char* s = (const char*)s_val; + if (!s) return (long long)strdup(""); + while (*s && isspace((unsigned char)*s)) s++; + long long len = strlen(s); + while (len > 0 && isspace((unsigned char)s[len - 1])) len--; + char* result = malloc(len + 1); + memcpy(result, s, len); + result[len] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long string_split(long long s_val, long long delim_val) { + const char* s = (const char*)s_val; + const char* delim = (const char*)delim_val; + if (!s || !delim) return create_list(); + long long list = create_list(); + long long dlen = strlen(delim); + if (dlen == 0) { append_list(list, s_val); return list; } + const char* p = s; + while (1) { + const char* found = strstr(p, delim); + long long partlen = found ? (found - p) : (long long)strlen(p); + char* part = malloc(partlen + 1); + memcpy(part, p, partlen); + part[partlen] = '\0'; + ep_gc_register(part, EP_OBJ_STRING); + append_list(list, (long long)part); + if (!found) break; + p = found + dlen; + } + return list; +} + +long long char_at(long long s_val, long long index) { + const char* s = (const char*)s_val; + if (!s || index < 0 || index >= (long long)strlen(s)) return 0; + return (unsigned char)s[index]; +} + +long long char_from_code(long long code) { + char* result = malloc(2); + result[0] = (char)code; + result[1] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long ep_abs(long long n) { + return n < 0 ? -n : n; +} + +// Auto-convert any value to string for string interpolation +long long ep_auto_to_string(long long val) { + // If the value is 0, return "0" + if (val == 0) return (long long)strdup("0"); + // Check if val is a GC-tracked string (heap-allocated) + EpGCObject* obj = ep_gc_find((void*)val); + if (obj && obj->kind == EP_OBJ_STRING) { + return val; // It's a known string pointer + } + // Check if val is a static string literal (in .rodata/.data segment) + // These aren't GC-tracked but ARE valid pointers. Use a safe probe: + // only dereference if the address is in a readable memory page. + if (val > 0x100000) { +#if defined(_WIN32) + // Windows: use VirtualQuery to safely probe pointer validity + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) { + const char* p = (const char*)(void*)val; + unsigned char first = (unsigned char)*p; + if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { + return val; // Readable memory, looks like a string + } + } +#elif defined(__APPLE__) + // macOS: use vm_read_overwrite to safely probe + char probe; + vm_size_t sz = 1; + kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz); + if (kr == KERN_SUCCESS) { + unsigned char first = (unsigned char)probe; + if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { + return val; // Readable memory, looks like a string + } + } +#else + // Linux: use write() to /dev/null as a safe pointer probe + // write() returns -1 with EFAULT for invalid pointers, no signal + int devnull = open("/dev/null", 1); // O_WRONLY + if (devnull >= 0) { + ssize_t r = write(devnull, (const void*)val, 1); + close(devnull); + if (r == 1) { + const char* p = (const char*)(void*)val; + unsigned char first = (unsigned char)*p; + if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) { + return val; + } + } + } +#endif + } + // Otherwise, convert integer to string + char* buf = (char*)malloc(32); + snprintf(buf, 32, "%lld", val); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + +long long ep_random_int(long long min, long long max) { + if (max <= min) return min; + /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */ + unsigned long long range = (unsigned long long)(max - min) + 1ULL; + unsigned long long limit = UINT64_MAX - (UINT64_MAX % range); + unsigned long long r; + do { + ep_secure_random_bytes((unsigned char*)&r, sizeof(r)); + } while (r >= limit); + return min + (long long)(r % range); +} + +// JSON built-in functions +static const char* json_skip_ws(const char* p) { + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + return p; +} + +static const char* json_skip_value(const char* p) { + p = json_skip_ws(p); + if (*p == '"') { + p++; + while (*p && *p != '"') { if (*p == '\\') p++; p++; } + if (*p == '"') p++; + } else if (*p == '{') { + int depth = 1; p++; + while (*p && depth > 0) { + if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; } + else if (*p == '{') { depth++; p++; } + else if (*p == '}') { depth--; p++; } + else p++; + } + } else if (*p == '[') { + int depth = 1; p++; + while (*p && depth > 0) { + if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; } + else if (*p == '[') { depth++; p++; } + else if (*p == ']') { depth--; p++; } + else p++; + } + } else { + while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\n') p++; + } + return p; +} + +static const char* json_find_key(const char* json, const char* key) { + const char* p = json_skip_ws(json); + if (*p != '{') return NULL; + p++; + while (*p) { + p = json_skip_ws(p); + if (*p == '}') return NULL; + if (*p != '"') return NULL; + p++; + const char* ks = p; + while (*p && *p != '"') { if (*p == '\\') p++; p++; } + size_t klen = p - ks; + if (*p == '"') p++; + p = json_skip_ws(p); + if (*p == ':') p++; + p = json_skip_ws(p); + if (klen == strlen(key) && strncmp(ks, key, klen) == 0) { + return p; + } + p = json_skip_value(p); + p = json_skip_ws(p); + if (*p == ',') p++; + } + return NULL; +} + +long long json_get_string(long long json_val, long long key_val) { + const char* json = (const char*)json_val; + const char* key = (const char*)key_val; + if (!json || !key) return (long long)strdup(""); + const char* val = json_find_key(json, key); + if (!val || *val != '"') return (long long)strdup(""); + val++; + const char* end = val; + while (*end && *end != '"') { if (*end == '\\') end++; end++; } + size_t len = end - val; + char* result = (char*)malloc(len + 1); + // Handle escape sequences + size_t di = 0; + const char* si = val; + while (si < end) { + if (*si == '\\' && si + 1 < end) { + si++; + switch (*si) { + case 'n': result[di++] = '\n'; break; + case 't': result[di++] = '\t'; break; + case 'r': result[di++] = '\r'; break; + case '"': result[di++] = '"'; break; + case '\\': result[di++] = '\\'; break; + default: result[di++] = *si; break; + } + } else { + result[di++] = *si; + } + si++; + } + result[di] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long json_get_int(long long json_val, long long key_val) { + const char* json = (const char*)json_val; + const char* key = (const char*)key_val; + if (!json || !key) return 0; + const char* val = json_find_key(json, key); + if (!val) return 0; + return atoll(val); +} + +long long json_get_bool(long long json_val, long long key_val) { + const char* json = (const char*)json_val; + const char* key = (const char*)key_val; + if (!json || !key) return 0; + const char* val = json_find_key(json, key); + if (!val) return 0; + if (strncmp(val, "true", 4) == 0) return 1; + return 0; +} + +// SHA-1 implementation (RFC 3174) for WebSocket handshake +static unsigned int sha1_left_rotate(unsigned int x, int n) { + return (x << n) | (x >> (32 - n)); +} + +long long ep_sha1(long long data_val) { + const unsigned char* data = (const unsigned char*)data_val; + if (!data) return (long long)strdup(""); + size_t len = strlen((const char*)data); + + unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0; + size_t new_len = len + 1; + while (new_len % 64 != 56) new_len++; + unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1); + memcpy(msg, data, len); + msg[len] = 0x80; + unsigned long long bits_len = (unsigned long long)len * 8; + for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8)); + + for (size_t offset = 0; offset < new_len + 8; offset += 64) { + unsigned int w[80]; + for (int i = 0; i < 16; i++) { + w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) | + ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3]; + } + for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1); + unsigned int a = h0, b = h1, c = h2, d = h3, e = h4; + for (int i = 0; i < 80; i++) { + unsigned int f, k; + if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; } + else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; } + else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; } + else { f = b ^ c ^ d; k = 0xCA62C1D6; } + unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i]; + e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp; + } + h0 += a; h1 += b; h2 += c; h3 += d; h4 += e; + } + free(msg); + + // Return Base64-encoded hash directly (for WebSocket handshake) + unsigned char hash[20]; + hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF; + hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF; + hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF; + hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF; + hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF; + + // Base64 encode the 20-byte hash + size_t b64_len = 4 * ((20 + 2) / 3); + char* result = (char*)malloc(b64_len + 1); + size_t j = 0; + for (size_t bi = 0; bi < 20; bi += 3) { + unsigned int n2 = ((unsigned int)hash[bi]) << 16; + if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8; + if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2]; + result[j++] = b64_table[(n2 >> 18) & 0x3F]; + result[j++] = b64_table[(n2 >> 12) & 0x3F]; + result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '='; + result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '='; + } + result[j] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +// Read exact N bytes from a socket +#ifdef __wasm__ +long long ep_net_recv_bytes(long long fd, long long count) { + (void)fd; (void)count; + return (long long)strdup(""); +} +#else +long long ep_net_recv_bytes(long long fd, long long count) { + if (count <= 0) return (long long)strdup(""); + char* buf = (char*)malloc(count + 1); +#ifdef _WIN32 + int total = 0; + while (total < (int)count) { + int n = recv((int)fd, buf + total, (int)(count - total), 0); + if (n <= 0) break; + total += n; + } +#else + ssize_t total = 0; + while (total < count) { + ssize_t n = recv((int)fd, buf + total, count - total, 0); + if (n <= 0) break; + total += n; + } +#endif + buf[total] = '\0'; + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} +#endif + +long long ep_get_args(void) { + long long list_ptr = create_list(); + for (int i = 0; i < ep_argc; i++) { + char* arg_copy = strdup(ep_argv[i]); + ep_gc_register(arg_copy, EP_OBJ_STRING); + append_list(list_ptr, (long long)arg_copy); + } + return list_ptr; +} + + +/* Built-in: string concatenation */ +long long concat(long long a, long long b) { + const char* sa = (const char*)a; + const char* sb = (const char*)b; + long long la = strlen(sa); + long long lb = strlen(sb); + char* result = malloc(la + lb + 1); + memcpy(result, sa, la); + memcpy(result + la, sb, lb); + result[la + lb] = '\0'; + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long int_to_string(long long val) { + char* buf = malloc(32); + snprintf(buf, 32, "%lld", val); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + +long long ep_int_to_str(long long val) { return int_to_string(val); } + +typedef struct { char* data; long long len; long long cap; } EpStringBuilder; + +long long ep_sb_create(long long dummy) { + (void)dummy; + EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder)); + sb->cap = 256; + sb->len = 0; + sb->data = (char*)malloc(sb->cap); + sb->data[0] = '\0'; + return (long long)sb; +} + +long long ep_sb_append(long long sb_ptr, long long str_ptr) { + EpStringBuilder* sb = (EpStringBuilder*)sb_ptr; + const char* s = (const char*)str_ptr; + if (!s) return sb_ptr; + long long slen = strlen(s); + while (sb->len + slen + 1 > sb->cap) { + sb->cap *= 2; + sb->data = (char*)realloc(sb->data, sb->cap); + } + memcpy(sb->data + sb->len, s, slen); + sb->len += slen; + sb->data[sb->len] = '\0'; + return sb_ptr; +} + +long long ep_sb_append_int(long long sb_ptr, long long val) { + char buf[32]; + snprintf(buf, sizeof(buf), "%lld", val); + return ep_sb_append(sb_ptr, (long long)buf); +} + +long long ep_sb_to_string(long long sb_ptr) { + EpStringBuilder* sb = (EpStringBuilder*)sb_ptr; + char* result = (char*)malloc(sb->len + 1); + memcpy(result, sb->data, sb->len + 1); + ep_gc_register(result, EP_OBJ_STRING); + free(sb->data); + free(sb); + return (long long)result; +} + +long long ep_sb_length(long long sb_ptr) { + return ((EpStringBuilder*)sb_ptr)->len; +} + +long long str_to_ptr(long long s) { return s; } +long long ptr_to_str(long long p) { + if (p == 0) return (long long)strdup(""); + char* copy = strdup((const char*)p); + ep_gc_register(copy, EP_OBJ_STRING); + return (long long)copy; +} + +long long peek_byte(long long ptr, long long offset) { + return (long long)((unsigned char*)ptr)[offset]; +} +long long poke_byte(long long ptr, long long offset, long long value) { + ((unsigned char*)ptr)[offset] = (unsigned char)value; + return 0; +} +long long alloc_bytes(long long size) { + return (long long)calloc((size_t)size, 1); +} +long long free_bytes(long long ptr) { + free((void*)ptr); + return 0; +} +long long list_to_bytes(long long list_ptr) { + long long len = length_list(list_ptr); + unsigned char* buf = (unsigned char*)malloc(len); + for (long long i = 0; i < len; i++) { + buf[i] = (unsigned char)get_list(list_ptr, i); + } + return (long long)buf; +} +long long bytes_to_list(long long ptr, long long len) { + long long list = create_list(); + unsigned char* buf = (unsigned char*)ptr; + for (long long i = 0; i < len; i++) { + append_list(list, (long long)buf[i]); + } + return list; +} + +long long ep_gc_get_minor_count() { + return ep_gc_minor_count; +} +long long ep_gc_get_major_count() { + return ep_gc_major_count; +} +long long ep_gc_get_nursery_count() { + return ep_gc_nursery_count; +} + +long long string_to_int(long long s) { + if (s == 0) return 0; + return atoll((const char*)s); +} + +long long read_line() { + char buf[4096]; + if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\0'; } + size_t len = strlen(buf); + if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0'; + char* result = strdup(buf); + ep_gc_register(result, EP_OBJ_STRING); + return (long long)result; +} + +long long read_int() { + long long val = 0; + scanf("%lld", &val); + while(getchar() != '\n'); + return val; +} + +long long read_float() { + double val = 0.0; + scanf("%lf", &val); + while(getchar() != '\n'); + long long result; memcpy(&result, &val, sizeof(double)); + return result; +} + +long long int_to_float(long long val) { + double d = (double)val; + long long result; memcpy(&result, &d, sizeof(double)); + return result; +} + +long long float_to_int(long long val) { + double d; memcpy(&d, &val, sizeof(double)); + return (long long)d; +} + +#define EP_STRUCT_MAX_SLOTS 8 + +/* External Function Prototypes (FFI) */ + + +/* User Function Prototypes */ +long long get_file_stem(long long); +long long get_file_dir(long long); +long long contains_string(long long, long long); +long long resolve_import_path(long long, long long); +long long parse_all_modules(long long, long long, long long, long long, long long, long long, long long, long long, long long); +long long _main(); +long long create_token(long long, long long, long long, long long); +long long get_token_type(long long); +long long get_token_value(long long); +long long get_token_line(long long); +long long get_token_col(long long); +long long match_next_word(long long, long long, long long); +long long lex_string_body(long long, long long, long long, long long, long long, long long, long long); +long long tokenize_source(long long); +long long parse_int(long long); +long long make_node_int(long long); +long long make_node_str(long long); +long long make_node_ident(long long); +long long make_node_binary(long long, long long, long long); +long long make_node_comp(long long, long long, long long); +long long make_node_call(long long, long long); +long long make_node_set(long long, long long); +long long make_node_return(long long); +long long make_node_display(long long); +long long make_node_if(long long, long long, long long); +long long make_node_repeat_while(long long, long long); +long long make_node_func(long long, long long, long long, long long); +long long make_node_program(long long, long long, long long, long long, long long, long long, long long, long long); +long long make_node_spawn(long long, long long); +long long make_node_send(long long, long long); +long long make_node_channel(); +long long make_node_receive(long long); +long long make_node_external(long long, long long, long long); +long long make_node_borrow(long long); +long long make_node_await(long long); +long long make_node_logical(long long, long long, long long); +long long make_node_field_access(long long, long long); +long long make_node_field_set(long long, long long, long long); +long long make_node_struct_create(long long, long long); +long long make_node_method_call(long long, long long, long long); +long long make_node_enum_create(long long, long long); +long long make_node_match(long long, long long); +long long make_node_for_each(long long, long long, long long); +long long make_node_break(); +long long make_node_continue(); +long long make_node_bool(long long); +long long make_node_unary_not(long long); +long long make_node_try(long long); +long long make_node_closure(long long, long long); +long long make_node_list_lit(long long); +long long make_node_expr_stmt(long long); +long long make_node_struct_def(long long, long long); +long long make_node_enum_def(long long, long long); +long long make_node_method_def(long long, long long, long long, long long); +long long make_node_trait_def(long long, long long); +long long make_node_trait_impl(long long, long long, long long); +long long create_parser_state(long long); +long long set_parser_error(long long); +long long get_parser_error(long long); +long long get_state_tokens(long long); +long long get_state_pos(long long); +long long set_state_pos(long long, long long); +long long get_eof_token(); +long long peek_token(long long); +long long peek_token_at(long long, long long); +long long advance_token(long long); +long long expect_token_type(long long, long long); +long long get_token_precedence(long long); +long long skip_newlines(long long); +long long is_uppercase_start(long long); +long long parse_param_list(long long); +long long parse_program(long long); +long long parse_struct_def(long long); +long long parse_enum_def(long long); +long long parse_method_def(long long); +long long parse_trait_def(long long); +long long parse_trait_impl(long long); +long long parse_function_async(long long, long long); +long long parse_block(long long); +long long parse_statement(long long); +long long parse_if_statement(long long); +long long parse_match_statement(long long); +long long parse_for_each_statement(long long); +long long parse_expr(long long, long long); +long long parse_prefix(long long); +long long parse_closure(long long); +long long parse_struct_create(long long); +long long parse_list_literal(long long); +long long map_get(long long, long long, long long); +long long map_contains_key(long long, long long); +long long collect_idents_expr(long long, long long); +long long collect_idents_stmts(long long, long long); +long long map_put(long long, long long, long long, long long); +long long field_slot_index(long long, long long); +long long string_concat(long long, long long); +long long get_fn_c_name(long long); +long long cg_int_to_str(long long); +long long escape_string(long long); +long long join_strings(long long); +long long create_codegen_state(); +long long is_builtin_c_func(long long, long long); +long long get_codegen_borrowed_keys(long long); +long long set_codegen_borrowed_keys(long long, long long); +long long get_codegen_borrowed_values(long long); +long long set_codegen_borrowed_values(long long, long long); +long long get_codegen_spawn_list(long long); +long long set_codegen_spawn_list(long long, long long); +long long get_codegen_spawn_index(long long); +long long set_codegen_spawn_index(long long, long long); +long long emit(long long, long long); +long long add_string_literal(long long, long long); +long long get_new_label(long long, long long); +long long analyze_return_types(long long, long long); +long long collect_var_types(long long, long long, long long, long long); +long long determine_ret_type(long long, long long, long long, long long); +long long infer_type(long long, long long, long long, long long); +long long is_global_var(long long); +long long cg_string_contains(long long, long long); +long long str_starts_with(long long, long long); +long long str_ends_with(long long, long long); +long long is_accessor_name(long long); +long long is_borrow_expr(long long, long long, long long); +long long scan_stmts_for_borrows(long long, long long, long long); +long long collect_borrowed_vars(long long, long long, long long, long long); +long long gen_function(long long, long long); +long long gen_statement(long long, long long, long long, long long); +long long gen_expr(long long, long long, long long, long long); +long long get_c_runtime_source(); +long long get_c_main_source(); +long long get_c_test_main_source(long long); +long long collect_spawns_in_stmts(long long, long long); +long long collect_all_spawns(long long); +long long clone_list(long long); +long long check_expr_reads(long long, long long, long long, long long, long long); +long long dec_borrow_count(long long, long long, long long); +long long inc_borrow_count(long long, long long, long long); +long long check_safety_stmts(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long); +long long analyze_safety(long long, long long); +long long generate_c(long long, long long); +long long ep_rt_core_0(); +long long ep_rt_core_1(); +long long ep_rt_core_2(); +long long ep_rt_core_3(); +long long ep_rt_core_4(); +long long ep_rt_core_5(); +long long ep_rt_core_6(); +long long ep_rt_core_7(); +long long ep_rt_core_8(); +long long ep_rt_core_9(); +long long ep_rt_core_10(); +long long ep_rt_core_11(); +long long ep_rt_core_12(); +long long ep_rt_core_13(); +long long ep_rt_core_14(); +long long ep_rt_core_15(); +long long ep_rt_core_16(); +long long ep_rt_core_17(); +long long ep_rt_core_18(); +long long ep_rt_core_19(); +long long ep_rt_core_20(); +long long ep_rt_core_21(); +long long ep_rt_core_22(); +long long ep_rt_core_23(); +long long ep_rt_core_24(); +long long ep_rt_core_25(); +long long ep_rt_core_26(); +long long ep_rt_core_27(); +long long ep_rt_core_28(); +long long ep_rt_core_29(); +long long ep_rt_core_30(); +long long ep_rt_core_31(); +long long ep_rt_builtins_0(); +long long ep_rt_builtins_1(); +long long get_shared_runtime_source(); + + +/* Thread Spawn Wrappers */ + + +/* EP_CLOSURE_BODIES */ +long long get_file_stem(long long path) { + long long len = 0; + long long last_slash = 0; + long long idx = 0; + long long ch = 0; + long long start = 0; + long long dot_pos = 0; + long long idx2 = 0; + long long stem_len = 0; + long long stem = 0; + long long ret_val = 0; + + ep_gc_push_root(&stem); + ep_gc_maybe_collect(); + + len = string_length((char*)path); + last_slash = (0LL - 1LL); + idx = 0LL; + while (idx < len) { + ch = get_character((char*)path, idx); + if (ch == 47LL) { + last_slash = idx; + } + idx = (idx + 1LL); + } + start = (last_slash + 1LL); + dot_pos = len; + idx2 = start; + while (idx2 < len) { + ch = get_character((char*)path, idx2); + if (ch == 46LL) { + dot_pos = idx2; + } + idx2 = (idx2 + 1LL); + } + stem_len = (dot_pos - start); + stem = (long long)substring((char*)path, start, stem_len); + ret_val = stem; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long get_file_dir(long long path) { + long long len = 0; + long long last_slash = 0; + long long idx = 0; + long long ch = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = string_length((char*)path); + last_slash = (0LL - 1LL); + idx = 0LL; + while (idx < len) { + ch = get_character((char*)path, idx); + if (ch == 47LL) { + last_slash = idx; + } + idx = (idx + 1LL); + } + if (last_slash < 0LL) { + ret_val = (long long)"./"; + goto L_cleanup; + } + ret_val = (long long)substring((char*)path, 0LL, (last_slash + 1LL)); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long contains_string(long long list, long long s) { + long long len = 0; + long long idx = 0; + long long item = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = length_list(list); + idx = 0LL; + while (idx < len) { + item = get_list(list, idx); + if ((strcmp((char*)string_concat(s, (long long)""), (char*)item) == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long resolve_import_path(long long current_file, long long import_path) { + long long std_path = 0; + long long std_path_ep = 0; + long long dir = 0; + long long resolved = 0; + long long len = 0; + long long ext = 0; + long long ret_val = 0; + + ep_gc_push_root(&std_path); + ep_gc_push_root(&std_path_ep); + ep_gc_push_root(&resolved); + ep_gc_push_root(&ext); + ep_gc_maybe_collect(); + + if ((((((((((((strcmp((char*)(long long)"math", (char*)import_path) == 0) || (strcmp((char*)(long long)"hash", (char*)import_path) == 0)) || (strcmp((char*)(long long)"net", (char*)import_path) == 0)) || (strcmp((char*)(long long)"json", (char*)import_path) == 0)) || (strcmp((char*)(long long)"string", (char*)import_path) == 0)) || (strcmp((char*)(long long)"sql", (char*)import_path) == 0)) || (strcmp((char*)(long long)"gui", (char*)import_path) == 0)) || (strcmp((char*)(long long)"crypto", (char*)import_path) == 0)) || (strcmp((char*)(long long)"fs", (char*)import_path) == 0)) || (strcmp((char*)(long long)"http", (char*)import_path) == 0)) || (strcmp((char*)(long long)"collections", (char*)import_path) == 0))) { + std_path = string_concat((long long)"stdlib/", import_path); + std_path_ep = string_concat(std_path, (long long)".ep"); + ret_val = std_path_ep; + goto L_cleanup; + } + dir = get_file_dir(current_file); + resolved = string_concat(dir, import_path); + len = string_length((char*)resolved); + if (len > 3LL) { + ext = (long long)substring((char*)resolved, (len - 3LL), 3LL); + if ((strcmp((char*)(long long)".ep", (char*)ext) == 0)) { + ret_val = resolved; + goto L_cleanup; + } + } + ret_val = string_concat(resolved, (long long)".ep"); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(4); + return ret_val; +} + +long long parse_all_modules(long long current_file, long long parsed_files, long long all_functions, long long all_externals, long long all_struct_defs, long long all_enum_defs, long long all_method_defs, long long all_trait_defs, long long all_trait_impls) { + long long has_parsed = 0; + long long ok = 0; + long long source = 0; + long long tokens = 0; + long long state = 0; + long long program_ast = 0; + long long imports = 0; + long long externals = 0; + long long funcs = 0; + long long externals_len = 0; + long long e_idx = 0; + long long ext = 0; + long long funcs_len = 0; + long long f_idx = 0; + long long func = 0; + long long prog_len = 0; + long long sd = 0; + long long sd_len = 0; + long long sd_idx = 0; + long long ed = 0; + long long ed_len = 0; + long long ed_idx = 0; + long long md = 0; + long long md_len = 0; + long long md_idx = 0; + long long td = 0; + long long td_len = 0; + long long td_idx = 0; + long long ti = 0; + long long ti_len = 0; + long long ti_idx = 0; + long long imp_len = 0; + long long i_idx = 0; + long long imp_pair = 0; + long long imp = 0; + long long imp_alias = 0; + long long resolved_path = 0; + long long status = 0; + long long mod_funcs = 0; + long long mod_externals = 0; + long long mf_len = 0; + long long mf_i = 0; + long long mfunc = 0; + long long acopy = 0; + long long mfl = 0; + long long mc_i = 0; + long long ok2 = 0; + long long aname = 0; + long long ok3 = 0; + long long ok4 = 0; + long long me_len = 0; + long long me_i = 0; + long long mext = 0; + long long ok5 = 0; + long long ecopy = 0; + long long mel = 0; + long long me_ci = 0; + long long ok6 = 0; + long long ename = 0; + long long ok7 = 0; + long long ok8 = 0; + long long ret_val = 0; + + ep_gc_push_root(&source); + ep_gc_push_root(&state); + ep_gc_push_root(&imp_alias); + ep_gc_push_root(&resolved_path); + ep_gc_push_root(&mod_funcs); + ep_gc_push_root(&mod_externals); + ep_gc_push_root(&aname); + ep_gc_push_root(&ename); + ep_gc_maybe_collect(); + + has_parsed = contains_string(parsed_files, current_file); + if (has_parsed == 1LL) { + ret_val = 0LL; + goto L_cleanup; + } + ok = append_list(parsed_files, current_file); + source = (long long)read_file_content((char*)current_file); + if (string_length((char*)source) == 0LL) { + printf("%s\n", (char*)(long long)"Compiler Error: Failed to read file or file is empty:"); + ok = display_string((char*)current_file); + ret_val = 1LL; + goto L_cleanup; + } + tokens = tokenize_source(source); + if (tokens == 0LL) { + printf("%s\n", (char*)(long long)"Compiler Error: Lexing failed (unterminated string or invalid token) in:"); + ok = display_string((char*)current_file); + ret_val = 1LL; + goto L_cleanup; + } + { + long long tmp_val = create_parser_state(tokens); + free_list(state); + state = tmp_val; + } + program_ast = parse_program(state); + if (get_parser_error(state) == 1LL) { + printf("%s\n", (char*)(long long)"Compiler Error: Parsing failed in:"); + ok = display_string((char*)current_file); + ret_val = 1LL; + goto L_cleanup; + } + imports = get_list(program_ast, 1LL); + externals = get_list(program_ast, 2LL); + funcs = get_list(program_ast, 3LL); + externals_len = length_list(externals); + e_idx = 0LL; + while (e_idx < externals_len) { + ext = get_list(externals, e_idx); + ok = append_list(all_externals, ext); + e_idx = (e_idx + 1LL); + } + funcs_len = length_list(funcs); + f_idx = 0LL; + while (f_idx < funcs_len) { + func = get_list(funcs, f_idx); + ok = append_list(all_functions, func); + f_idx = (f_idx + 1LL); + } + prog_len = length_list(program_ast); + if (prog_len > 4LL) { + sd = get_list(program_ast, 4LL); + sd_len = length_list(sd); + sd_idx = 0LL; + while (sd_idx < sd_len) { + ok = append_list(all_struct_defs, get_list(sd, sd_idx)); + sd_idx = (sd_idx + 1LL); + } + } + if (prog_len > 5LL) { + ed = get_list(program_ast, 5LL); + ed_len = length_list(ed); + ed_idx = 0LL; + while (ed_idx < ed_len) { + ok = append_list(all_enum_defs, get_list(ed, ed_idx)); + ed_idx = (ed_idx + 1LL); + } + } + if (prog_len > 6LL) { + md = get_list(program_ast, 6LL); + md_len = length_list(md); + md_idx = 0LL; + while (md_idx < md_len) { + ok = append_list(all_method_defs, get_list(md, md_idx)); + md_idx = (md_idx + 1LL); + } + } + if (prog_len > 7LL) { + td = get_list(program_ast, 7LL); + td_len = length_list(td); + td_idx = 0LL; + while (td_idx < td_len) { + ok = append_list(all_trait_defs, get_list(td, td_idx)); + td_idx = (td_idx + 1LL); + } + } + if (prog_len > 8LL) { + ti = get_list(program_ast, 8LL); + ti_len = length_list(ti); + ti_idx = 0LL; + while (ti_idx < ti_len) { + ok = append_list(all_trait_impls, get_list(ti, ti_idx)); + ti_idx = (ti_idx + 1LL); + } + } + imp_len = length_list(imports); + i_idx = 0LL; + while (i_idx < imp_len) { + imp_pair = get_list(imports, i_idx); + imp = get_list(imp_pair, 0LL); + imp_alias = string_concat(get_list(imp_pair, 1LL), (long long)""); + resolved_path = resolve_import_path(current_file, imp); + if (string_length((char*)imp_alias) == 0LL) { + status = parse_all_modules(resolved_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls); + if (status != 0LL) { + ret_val = status; + goto L_cleanup; + } + } else { + { + long long tmp_val = create_list(); + free_list(mod_funcs); + mod_funcs = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(mod_externals); + mod_externals = tmp_val; + } + status = parse_all_modules(resolved_path, parsed_files, mod_funcs, mod_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls); + if (status != 0LL) { + ret_val = status; + goto L_cleanup; + } + mf_len = length_list(mod_funcs); + mf_i = 0LL; + while (mf_i < mf_len) { + mfunc = get_list(mod_funcs, mf_i); + ok = append_list(all_functions, mfunc); + acopy = (create_list() + 0LL); + mfl = length_list(mfunc); + mc_i = 0LL; + while (mc_i < mfl) { + ok2 = append_list(acopy, get_list(mfunc, mc_i)); + mc_i = (mc_i + 1LL); + } + aname = string_concat(imp_alias, (long long)"_"); + aname = string_concat(aname, get_list(mfunc, 1LL)); + ok3 = set_list(acopy, 1LL, aname); + ok4 = append_list(all_functions, acopy); + mf_i = (mf_i + 1LL); + } + me_len = length_list(mod_externals); + me_i = 0LL; + while (me_i < me_len) { + mext = get_list(mod_externals, me_i); + ok5 = append_list(all_externals, mext); + ecopy = (create_list() + 0LL); + mel = length_list(mext); + me_ci = 0LL; + while (me_ci < mel) { + ok6 = append_list(ecopy, get_list(mext, me_ci)); + me_ci = (me_ci + 1LL); + } + ename = string_concat(imp_alias, (long long)"_"); + ename = string_concat(ename, get_list(mext, 1LL)); + ok7 = set_list(ecopy, 1LL, ename); + ok8 = append_list(all_externals, ecopy); + me_i = (me_i + 1LL); + } + } + i_idx = (i_idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(8); + free_list(state); + free_list(mod_funcs); + free_list(mod_externals); + return ret_val; +} + +long long _main() { + long long arg_count = 0; + long long first_arg = 0; + long long is_test_mode = 0; + long long input_path = 0; + long long stem = 0; + long long all_functions = 0; + long long all_externals = 0; + long long all_struct_defs = 0; + long long all_enum_defs = 0; + long long all_method_defs = 0; + long long all_trait_defs = 0; + long long all_trait_impls = 0; + long long parsed_files = 0; + long long status = 0; + long long f_names = 0; + long long all_len = 0; + long long idx = 0; + long long duplicate_found = 0; + long long func = 0; + long long name = 0; + long long ok = 0; + long long empty_imports = 0; + long long program_ast = 0; + long long c_code = 0; + long long c_path = 0; + long long compile_cmd = 0; + long long pf_len = 0; + long long pf_idx = 0; + long long pf = 0; + long long pf_str = 0; + long long pf_len_str = 0; + long long ext_sql = 0; + long long ext_crypto = 0; + long long ext_gui = 0; + long long ext_cry = 0; + long long ret_val = 0; + + ep_gc_push_root(&stem); + ep_gc_push_root(&all_functions); + ep_gc_push_root(&all_externals); + ep_gc_push_root(&all_struct_defs); + ep_gc_push_root(&all_enum_defs); + ep_gc_push_root(&all_method_defs); + ep_gc_push_root(&all_trait_defs); + ep_gc_push_root(&all_trait_impls); + ep_gc_push_root(&parsed_files); + ep_gc_push_root(&f_names); + ep_gc_push_root(&empty_imports); + ep_gc_push_root(&program_ast); + ep_gc_push_root(&c_path); + ep_gc_push_root(&compile_cmd); + ep_gc_push_root(&pf_str); + ep_gc_push_root(&ext_sql); + ep_gc_push_root(&ext_crypto); + ep_gc_push_root(&ext_gui); + ep_gc_push_root(&ext_cry); + ep_gc_maybe_collect(); + + arg_count = get_argument_count(); + if (arg_count < 2LL) { + printf("%s\n", (char*)(long long)"Usage: epc or epc test "); + ret_val = 1LL; + goto L_cleanup; + } + first_arg = (long long)get_argument(1LL); + is_test_mode = 0LL; + input_path = (long long)get_argument(1LL); + if ((strcmp((char*)(long long)"test", (char*)first_arg) == 0)) { + if (arg_count < 3LL) { + printf("%s\n", (char*)(long long)"Usage: epc test "); + ret_val = 1LL; + goto L_cleanup; + } + is_test_mode = 1LL; + input_path = (long long)get_argument(2LL); + } + stem = get_file_stem(input_path); + printf("%s\n", (char*)(long long)"[1/3] Tokenizing and Parsing..."); + { + long long tmp_val = create_list(); + free_list(all_functions); + all_functions = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(all_externals); + all_externals = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(all_struct_defs); + all_struct_defs = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(all_enum_defs); + all_enum_defs = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(all_method_defs); + all_method_defs = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(all_trait_defs); + all_trait_defs = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(all_trait_impls); + all_trait_impls = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(parsed_files); + parsed_files = tmp_val; + } + status = parse_all_modules(input_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls); + if (status != 0LL) { + ret_val = 1LL; + goto L_cleanup; + } + { + long long tmp_val = create_list(); + free_list(f_names); + f_names = tmp_val; + } + all_len = length_list(all_functions); + idx = 0LL; + duplicate_found = 0LL; + while ((idx < all_len && duplicate_found == 0LL)) { + func = get_list(all_functions, idx); + name = get_list(func, 1LL); + if (contains_string(f_names, name) == 1LL) { + printf("%s\n", (char*)(long long)"Compiler Error: Function is defined multiple times:"); + ok = display_string((char*)name); + duplicate_found = 1LL; + } else { + ok = append_list(f_names, name); + } + idx = (idx + 1LL); + } + if (duplicate_found == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + { + long long tmp_val = create_list(); + free_list(empty_imports); + empty_imports = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(program_ast); + program_ast = tmp_val; + } + ok = append_list(program_ast, 13LL); + ok = append_list(program_ast, empty_imports); + ok = append_list(program_ast, all_externals); + ok = append_list(program_ast, all_functions); + ok = append_list(program_ast, all_struct_defs); + ok = append_list(program_ast, all_enum_defs); + ok = append_list(program_ast, all_method_defs); + ok = append_list(program_ast, all_trait_defs); + ok = append_list(program_ast, all_trait_impls); + printf("%s\n", (char*)(long long)"[2/3] Generating C Source..."); + c_code = generate_c(program_ast, is_test_mode); + if (string_length((char*)c_code) == 0LL) { + printf("%s\n", (char*)(long long)"Compilation failed due to errors."); + ret_val = 1LL; + goto L_cleanup; + } + c_path = string_concat(stem, (long long)"_compiled.c"); + ok = write_file_content((char*)c_path, (char*)c_code); + printf("%s\n", (char*)(long long)"[3/3] Compiling and Linking via Clang..."); + compile_cmd = (long long)"clang "; + compile_cmd = string_concat(compile_cmd, c_path); + compile_cmd = string_concat(compile_cmd, (long long)" -o "); + compile_cmd = string_concat(compile_cmd, stem); + compile_cmd = string_concat(compile_cmd, (long long)" -lpthread"); + pf_len = length_list(parsed_files); + pf_idx = 0LL; + while (pf_idx < pf_len) { + pf = get_list(parsed_files, pf_idx); + pf_str = string_concat(pf, (long long)""); + pf_len_str = string_length((char*)pf_str); + if (pf_len_str > 5LL) { + ext_sql = (long long)substring((char*)pf_str, (pf_len_str - 6LL), 6LL); + if ((strcmp((char*)(long long)"sql.ep", (char*)ext_sql) == 0)) { + compile_cmd = string_concat(compile_cmd, (long long)" -DEP_HAS_SQLITE -lsqlite3"); + } + } + if (pf_len_str > 8LL) { + ext_crypto = (long long)substring((char*)pf_str, (pf_len_str - 9LL), 9LL); + if ((strcmp((char*)(long long)"crypto.ep", (char*)ext_crypto) == 0)) { + compile_cmd = string_concat(compile_cmd, (long long)" -L/opt/homebrew/opt/openssl/lib -lcrypto"); + } + } + if (pf_len_str > 5LL) { + ext_gui = (long long)substring((char*)pf_str, (pf_len_str - 6LL), 6LL); + if ((strcmp((char*)(long long)"gui.ep", (char*)ext_gui) == 0)) { + compile_cmd = string_concat(compile_cmd, (long long)" -lraylib"); + } + } + if (pf_len_str > 8LL) { + ext_cry = (long long)substring((char*)pf_str, (pf_len_str - 9LL), 9LL); + if ((strcmp((char*)(long long)"crypto.ep", (char*)ext_cry) == 0)) { + compile_cmd = string_concat(compile_cmd, (long long)" -L/opt/homebrew/opt/openssl/lib -lcrypto"); + } + } + pf_idx = (pf_idx + 1LL); + } + status = run_command((char*)compile_cmd); + if (status == 0LL) { + printf("%s\n", (char*)(long long)"Self-hosted compilation successful!"); + ret_val = 0LL; + goto L_cleanup; + } else { + printf("%s\n", (char*)(long long)"Compilation failed."); + ret_val = 1LL; + goto L_cleanup; + } +L_cleanup: + ep_gc_pop_roots(19); + free_list(all_functions); + free_list(all_externals); + free_list(all_struct_defs); + free_list(all_enum_defs); + free_list(all_method_defs); + free_list(all_trait_defs); + free_list(all_trait_impls); + free_list(parsed_files); + free_list(f_names); + free_list(empty_imports); + free_list(program_ast); + return ret_val; +} + +long long create_token(long long type, long long value, long long line, long long col) { + long long tok = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&tok); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(tok); + tok = tmp_val; + } + ok = append_list(tok, type); + ok = append_list(tok, value); + ok = append_list(tok, line); + ok = append_list(tok, col); + ret_val = tok; + tok = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(tok); + return ret_val; +} + +long long get_token_type(long long tok) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(tok, 0LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_token_value(long long tok) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(tok, 1LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_token_line(long long tok) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(tok, 2LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_token_col(long long tok) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(tok, 3LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long match_next_word(long long source, long long start_pos, long long next_word) { + long long p = 0; + long long s_len = 0; + long long loop = 0; + long long ch = 0; + long long nw_len = 0; + long long idx = 0; + long long matches = 0; + long long ch1 = 0; + long long ch2 = 0; + long long next_ch = 0; + long long is_id_part = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + p = start_pos; + s_len = string_length((char*)source); + loop = 1LL; + while ((p < s_len && loop == 1LL)) { + ch = get_character((char*)source, p); + if ((ch == 32LL || ch == 9LL)) { + p = (p + 1LL); + } else { + loop = 0LL; + } + } + nw_len = string_length((char*)next_word); + if ((p + nw_len) > s_len) { + ret_val = 0LL; + goto L_cleanup; + } + idx = 0LL; + matches = 1LL; + while ((idx < nw_len && matches == 1LL)) { + ch1 = get_character((char*)source, (p + idx)); + ch2 = get_character((char*)next_word, idx); + if (ch1 != ch2) { + matches = 0LL; + } + idx = (idx + 1LL); + } + if (matches == 1LL) { + next_ch = get_character((char*)source, (p + nw_len)); + is_id_part = ((((next_ch > 96LL && next_ch < 123LL) || (next_ch > 64LL && next_ch < 91LL)) || (next_ch > 47LL && next_ch < 58LL)) || next_ch == 95LL); + if (is_id_part == 0LL) { + ret_val = (p + nw_len); + goto L_cleanup; + } + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long lex_string_body(long long source, long long pos0, long long source_len, long long tokens, long long current_line, long long start_col, long long is_fstring) { + long long pos = 0; + long long col = 0; + long long opens = 0; + long long str_chars = 0; + long long closed = 0; + long long err = 0; + long long looping = 0; + long long ch = 0; + long long lit = 0; + long long t1 = 0; + long long ok1 = 0; + long long t2 = 0; + long long ok2 = 0; + long long t3 = 0; + long long ok3 = 0; + long long t4 = 0; + long long ok4 = 0; + long long t5 = 0; + long long ok5 = 0; + long long t6 = 0; + long long ok6 = 0; + long long t7 = 0; + long long ok7 = 0; + long long t8 = 0; + long long ok8 = 0; + long long depth = 0; + long long expr_chars = 0; + long long eloop = 0; + long long ec = 0; + long long okq = 0; + long long sloop = 0; + long long sc = 0; + long long okq2 = 0; + long long qc = 0; + long long okq3 = 0; + long long oka = 0; + long long okb = 0; + long long expr_src = 0; + long long expr_tokens = 0; + long long et_len = 0; + long long ei = 0; + long long et = 0; + long long ett = 0; + long long okc = 0; + long long t9 = 0; + long long ok9 = 0; + long long t10 = 0; + long long ok10 = 0; + long long esc_ch = 0; + long long oke = 0; + long long oke2 = 0; + long long okp = 0; + long long lit2 = 0; + long long tokf = 0; + long long okf = 0; + long long ci = 0; + long long tcp = 0; + long long okcp = 0; + long long res = 0; + long long okr1 = 0; + long long okr2 = 0; + long long okr3 = 0; + long long ret_val = 0; + + ep_gc_push_root(&str_chars); + ep_gc_push_root(&lit); + ep_gc_push_root(&expr_chars); + ep_gc_push_root(&expr_src); + ep_gc_push_root(&lit2); + ep_gc_push_root(&res); + ep_gc_maybe_collect(); + + pos = (pos0 + 1LL); + col = (start_col + 1LL); + opens = 0LL; + { + long long tmp_val = create_list(); + free_list(str_chars); + str_chars = tmp_val; + } + closed = 0LL; + err = 0LL; + looping = 1LL; + while ((pos < source_len && looping == 1LL)) { + ch = get_character((char*)source, pos); + if (ch == 34LL) { + closed = 1LL; + looping = 0LL; + pos = (pos + 1LL); + col = (col + 1LL); + } else { + if ((ch == 123LL && is_fstring == 1LL)) { + pos = (pos + 1LL); + col = (col + 1LL); + lit = (long long)string_from_list(str_chars); + { + long long tmp_val = create_list(); + free_list(str_chars); + str_chars = tmp_val; + } + if (string_length((char*)lit) != 0LL) { + t1 = (create_token(27LL, (long long)"concat", current_line, col) + 0LL); + ok1 = append_list(tokens, t1); + t2 = (create_token(23LL, (long long)"(", current_line, col) + 0LL); + ok2 = append_list(tokens, t2); + t3 = (create_token(26LL, lit, current_line, col) + 0LL); + ok3 = append_list(tokens, t3); + t4 = (create_token(11LL, (long long)"and", current_line, col) + 0LL); + ok4 = append_list(tokens, t4); + opens = (opens + 1LL); + } + t5 = (create_token(27LL, (long long)"concat", current_line, col) + 0LL); + ok5 = append_list(tokens, t5); + t6 = (create_token(23LL, (long long)"(", current_line, col) + 0LL); + ok6 = append_list(tokens, t6); + t7 = (create_token(27LL, (long long)"ep_auto_to_string", current_line, col) + 0LL); + ok7 = append_list(tokens, t7); + t8 = (create_token(23LL, (long long)"(", current_line, col) + 0LL); + ok8 = append_list(tokens, t8); + depth = 1LL; + { + long long tmp_val = create_list(); + free_list(expr_chars); + expr_chars = tmp_val; + } + eloop = 1LL; + while ((pos < source_len && eloop == 1LL)) { + ec = get_character((char*)source, pos); + if (ec == 34LL) { + okq = append_list(expr_chars, ec); + pos = (pos + 1LL); + col = (col + 1LL); + sloop = 1LL; + while ((pos < source_len && sloop == 1LL)) { + sc = get_character((char*)source, pos); + okq2 = append_list(expr_chars, sc); + pos = (pos + 1LL); + col = (col + 1LL); + if (sc == 34LL) { + sloop = 0LL; + } else { + if (sc == 92LL) { + if (pos < source_len) { + qc = get_character((char*)source, pos); + okq3 = append_list(expr_chars, qc); + pos = (pos + 1LL); + col = (col + 1LL); + } + } + } + } + } else { + if (ec == 125LL) { + depth = (depth - 1LL); + if (depth == 0LL) { + pos = (pos + 1LL); + col = (col + 1LL); + eloop = 0LL; + } else { + oka = append_list(expr_chars, ec); + pos = (pos + 1LL); + col = (col + 1LL); + } + } else { + if (ec == 123LL) { + depth = (depth + 1LL); + } + okb = append_list(expr_chars, ec); + pos = (pos + 1LL); + col = (col + 1LL); + } + } + } + if (eloop == 1LL) { + printf("%s\n", (char*)(long long)"Lexer Error: Unterminated interpolation in f-string"); + err = 1LL; + looping = 0LL; + } else { + expr_src = (long long)string_from_list(expr_chars); + expr_tokens = (tokenize_source(expr_src) + 0LL); + if (expr_tokens == 0LL) { + printf("%s\n", (char*)(long long)"Lexer Error: Invalid expression inside f-string interpolation"); + err = 1LL; + looping = 0LL; + } else { + et_len = length_list(expr_tokens); + ei = 0LL; + while (ei < et_len) { + et = get_list(expr_tokens, ei); + ett = get_token_type(et); + if ((((ett != 28LL && ett != 29LL) && ett != 30LL) && ett != 31LL)) { + okc = append_list(tokens, et); + } + ei = (ei + 1LL); + } + t9 = (create_token(24LL, (long long)")", current_line, col) + 0LL); + ok9 = append_list(tokens, t9); + t10 = (create_token(11LL, (long long)"and", current_line, col) + 0LL); + ok10 = append_list(tokens, t10); + opens = (opens + 1LL); + } + } + } else { + if (ch == 92LL) { + pos = (pos + 1LL); + col = (col + 1LL); + if (pos < source_len) { + esc_ch = get_character((char*)source, pos); + if (esc_ch == 110LL) { + oke = append_list(str_chars, 10LL); + } else { + if (esc_ch == 116LL) { + oke = append_list(str_chars, 9LL); + } else { + if (esc_ch == 114LL) { + oke = append_list(str_chars, 13LL); + } else { + if (esc_ch == 34LL) { + oke = append_list(str_chars, 34LL); + } else { + if (esc_ch == 92LL) { + oke = append_list(str_chars, 92LL); + } else { + if (esc_ch == 123LL) { + oke = append_list(str_chars, 123LL); + } else { + if (esc_ch == 125LL) { + oke = append_list(str_chars, 125LL); + } else { + oke = append_list(str_chars, 92LL); + oke2 = append_list(str_chars, esc_ch); + } + } + } + } + } + } + } + pos = (pos + 1LL); + col = (col + 1LL); + } else { + printf("%s\n", (char*)(long long)"Lexer Error: Unterminated string literal at escape sequence"); + err = 1LL; + looping = 0LL; + } + } else { + if ((ch == 10LL || ch == 13LL)) { + printf("%s\n", (char*)(long long)"Lexer Error: Unterminated string literal"); + err = 1LL; + looping = 0LL; + } else { + okp = append_list(str_chars, ch); + pos = (pos + 1LL); + col = (col + 1LL); + } + } + } + } + } + if (closed == 0LL) { + if (err == 0LL) { + printf("%s\n", (char*)(long long)"Lexer Error: Unterminated string literal"); + err = 1LL; + } + } + if (err == 0LL) { + lit2 = (long long)string_from_list(str_chars); + tokf = (create_token(26LL, lit2, current_line, start_col) + 0LL); + okf = append_list(tokens, tokf); + if (is_fstring == 1LL) { + ci = 0LL; + while (ci < opens) { + tcp = (create_token(24LL, (long long)")", current_line, col) + 0LL); + okcp = append_list(tokens, tcp); + ci = (ci + 1LL); + } + } + } + { + long long tmp_val = create_list(); + free_list(res); + res = tmp_val; + } + okr1 = append_list(res, pos); + okr2 = append_list(res, col); + okr3 = append_list(res, err); + ret_val = res; + res = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(6); + free_list(str_chars); + free_list(expr_chars); + free_list(res); + return ret_val; +} + +long long tokenize_source(long long source) { + long long tokens = 0; + long long source_len = 0; + long long pos = 0; + long long current_line = 0; + long long current_col = 0; + long long indent_stack = 0; + long long ok = 0; + long long at_line_start = 0; + long long spaces = 0; + long long space_loop = 0; + long long ch = 0; + long long next_ch = 0; + long long stack_len = 0; + long long last_indent = 0; + long long tok = 0; + long long loop_dedent = 0; + long long s_len = 0; + long long top_indent = 0; + long long popped = 0; + long long dummy = 0; + long long c = 0; + long long tokens_len = 0; + long long should_emit_nl = 0; + long long last_tok = 0; + long long num_start = 0; + long long num_len = 0; + long long num_str = 0; + long long is_id_start = 0; + long long id_start = 0; + long long id_loop = 0; + long long is_id_part = 0; + long long id_len = 0; + long long id_str = 0; + long long tok_type = 0; + long long is_multi_phrase = 0; + long long next_p = 0; + long long next_p2 = 0; + long long next_p3 = 0; + long long start_col = 0; + long long is_fstring = 0; + long long tok_count = 0; + long long sres = 0; + long long sym_type = 0; + long long sym_val = 0; + long long sym_len = 0; + long long next_c = 0; + long long ret_val = 0; + + ep_gc_push_root(&tokens); + ep_gc_push_root(&indent_stack); + ep_gc_push_root(&num_str); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(tokens); + tokens = tmp_val; + } + source_len = string_length((char*)source); + pos = 0LL; + current_line = 1LL; + current_col = 1LL; + { + long long tmp_val = create_list(); + free_list(indent_stack); + indent_stack = tmp_val; + } + ok = append_list(indent_stack, 0LL); + at_line_start = 1LL; + while (pos < source_len) { + if (at_line_start == 1LL) { + spaces = 0LL; + space_loop = 1LL; + while ((pos < source_len && space_loop == 1LL)) { + ch = get_character((char*)source, pos); + if (ch == 32LL) { + spaces = (spaces + 1LL); + pos = (pos + 1LL); + current_col = (current_col + 1LL); + } else { + if (ch == 9LL) { + spaces = (spaces + 4LL); + pos = (pos + 1LL); + current_col = (current_col + 4LL); + } else { + space_loop = 0LL; + } + } + } + next_ch = get_character((char*)source, pos); + if ((((next_ch != 10LL && next_ch != 13LL) && next_ch != 35LL) && pos < source_len)) { + stack_len = length_list(indent_stack); + last_indent = get_list(indent_stack, (stack_len - 1LL)); + if (spaces > last_indent) { + ok = append_list(indent_stack, spaces); + tok = (create_token(29LL, (long long)"INDENT", current_line, current_col) + 0LL); + ok = append_list(tokens, tok); + } else { + if (spaces < last_indent) { + loop_dedent = 1LL; + while (loop_dedent == 1LL) { + s_len = length_list(indent_stack); + top_indent = get_list(indent_stack, (s_len - 1LL)); + if (spaces < top_indent) { + popped = pop_list(indent_stack); + tok = (create_token(30LL, (long long)"DEDENT", current_line, current_col) + 0LL); + ok = append_list(tokens, tok); + } else { + loop_dedent = 0LL; + } + } + } + } + } + at_line_start = 0LL; + } + if (pos > (source_len - 1LL)) { + dummy = 0LL; + } else { + c = get_character((char*)source, pos); + if ((c == 32LL || c == 9LL)) { + pos = (pos + 1LL); + current_col = (current_col + 1LL); + } else { + if ((c == 10LL || c == 13LL)) { + pos = (pos + 1LL); + if ((c == 13LL && get_character((char*)source, pos) == 10LL)) { + pos = (pos + 1LL); + } + tokens_len = length_list(tokens); + should_emit_nl = 1LL; + if (tokens_len > 0LL) { + last_tok = get_list(tokens, (tokens_len - 1LL)); + if (get_token_type(last_tok) == 28LL) { + should_emit_nl = 0LL; + } + } + if ((should_emit_nl == 1LL && tokens_len > 0LL)) { + tok = (create_token(28LL, (long long)"\n", current_line, current_col) + 0LL); + ok = append_list(tokens, tok); + } + current_line = (current_line + 1LL); + current_col = 1LL; + at_line_start = 1LL; + } else { + if (c == 35LL) { + pos = (pos + 1LL); + while (((pos < source_len && get_character((char*)source, pos) != 10LL) && get_character((char*)source, pos) != 13LL)) { + pos = (pos + 1LL); + } + } else { + if ((c > 47LL && c < 58LL)) { + num_start = pos; + while (((pos < source_len && get_character((char*)source, pos) > 47LL) && get_character((char*)source, pos) < 58LL)) { + pos = (pos + 1LL); + current_col = (current_col + 1LL); + } + num_len = (pos - num_start); + num_str = (long long)substring((char*)source, num_start, num_len); + tok = (create_token(25LL, num_str, current_line, (current_col - num_len)) + 0LL); + ok = append_list(tokens, tok); + } else { + is_id_start = (((c > 96LL && c < 123LL) || (c > 64LL && c < 91LL)) || c == 95LL); + if (is_id_start) { + id_start = pos; + id_loop = 1LL; + while ((pos < source_len && id_loop == 1LL)) { + ch = get_character((char*)source, pos); + is_id_part = ((((ch > 96LL && ch < 123LL) || (ch > 64LL && ch < 91LL)) || (ch > 47LL && ch < 58LL)) || ch == 95LL); + if (is_id_part) { + pos = (pos + 1LL); + current_col = (current_col + 1LL); + } else { + id_loop = 0LL; + } + } + id_len = (pos - id_start); + id_str = (long long)substring((char*)source, id_start, id_len); + tok_type = 27LL; + is_multi_phrase = 0LL; + if ((strcmp((char*)(long long)"multiplied", (char*)id_str) == 0)) { + next_p = match_next_word(source, pos, (long long)"by"); + if (next_p > 0LL) { + tok_type = 14LL; + id_str = (long long)"*"; + current_col = (current_col + (next_p - pos)); + pos = next_p; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + if ((strcmp((char*)(long long)"divided", (char*)id_str) == 0)) { + next_p = match_next_word(source, pos, (long long)"by"); + if (next_p > 0LL) { + tok_type = 15LL; + id_str = (long long)"/"; + current_col = (current_col + (next_p - pos)); + pos = next_p; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + if ((strcmp((char*)(long long)"is", (char*)id_str) == 0)) { + next_p = match_next_word(source, pos, (long long)"not"); + if (next_p > 0LL) { + next_p2 = match_next_word(source, next_p, (long long)"equal"); + if (next_p2 > 0LL) { + next_p3 = match_next_word(source, next_p2, (long long)"to"); + if (next_p3 > 0LL) { + tok_type = 19LL; + id_str = (long long)"!="; + current_col = (current_col + (next_p3 - pos)); + pos = next_p3; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + next_p = match_next_word(source, pos, (long long)"less"); + if (next_p > 0LL) { + next_p2 = match_next_word(source, next_p, (long long)"than"); + if (next_p2 > 0LL) { + tok_type = 16LL; + id_str = (long long)"<"; + current_col = (current_col + (next_p2 - pos)); + pos = next_p2; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + next_p = match_next_word(source, pos, (long long)"greater"); + if (next_p > 0LL) { + next_p2 = match_next_word(source, next_p, (long long)"than"); + if (next_p2 > 0LL) { + tok_type = 17LL; + id_str = (long long)">"; + current_col = (current_col + (next_p2 - pos)); + pos = next_p2; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + next_p = match_next_word(source, pos, (long long)"equal"); + if (next_p > 0LL) { + next_p2 = match_next_word(source, next_p, (long long)"to"); + if (next_p2 > 0LL) { + tok_type = 18LL; + id_str = (long long)"=="; + current_col = (current_col + (next_p2 - pos)); + pos = next_p2; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + tok_type = 72LL; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + if ((strcmp((char*)(long long)"and", (char*)id_str) == 0)) { + next_p = match_next_word(source, pos, (long long)"also"); + if (next_p > 0LL) { + tok_type = 20LL; + id_str = (long long)"&&"; + current_col = (current_col + (next_p - pos)); + pos = next_p; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + if ((strcmp((char*)(long long)"give", (char*)id_str) == 0)) { + next_p = match_next_word(source, pos, (long long)"back"); + if (next_p > 0LL) { + tok_type = 6LL; + id_str = (long long)"return"; + current_col = (current_col + (next_p - pos)); + pos = next_p; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + if ((strcmp((char*)(long long)"or", (char*)id_str) == 0)) { + next_p = match_next_word(source, pos, (long long)"else"); + if (next_p > 0LL) { + tok_type = 21LL; + id_str = (long long)"||"; + current_col = (current_col + (next_p - pos)); + pos = next_p; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { + if ((strcmp((char*)(long long)"define", (char*)id_str) == 0)) { + tok_type = 1LL; + } + if ((strcmp((char*)(long long)"set", (char*)id_str) == 0)) { + tok_type = 2LL; + } + if ((strcmp((char*)(long long)"to", (char*)id_str) == 0)) { + tok_type = 3LL; + } + if ((strcmp((char*)(long long)"if", (char*)id_str) == 0)) { + tok_type = 4LL; + } + if ((strcmp((char*)(long long)"else", (char*)id_str) == 0)) { + tok_type = 5LL; + } + if ((strcmp((char*)(long long)"return", (char*)id_str) == 0)) { + tok_type = 6LL; + } + if ((strcmp((char*)(long long)"display", (char*)id_str) == 0)) { + tok_type = 7LL; + } + if ((strcmp((char*)(long long)"repeat", (char*)id_str) == 0)) { + tok_type = 8LL; + } + if ((strcmp((char*)(long long)"while", (char*)id_str) == 0)) { + tok_type = 9LL; + } + if ((strcmp((char*)(long long)"with", (char*)id_str) == 0)) { + tok_type = 10LL; + } + if ((strcmp((char*)(long long)"and", (char*)id_str) == 0)) { + tok_type = 11LL; + } + if ((strcmp((char*)(long long)"plus", (char*)id_str) == 0)) { + tok_type = 12LL; + } + if ((strcmp((char*)(long long)"minus", (char*)id_str) == 0)) { + tok_type = 13LL; + } + if ((strcmp((char*)(long long)"equals", (char*)id_str) == 0)) { + tok_type = 18LL; + } + if ((strcmp((char*)(long long)"import", (char*)id_str) == 0)) { + tok_type = 32LL; + } + if ((strcmp((char*)(long long)"spawn", (char*)id_str) == 0)) { + tok_type = 33LL; + } + if ((strcmp((char*)(long long)"channel", (char*)id_str) == 0)) { + tok_type = 34LL; + } + if ((strcmp((char*)(long long)"send", (char*)id_str) == 0)) { + tok_type = 35LL; + } + if ((strcmp((char*)(long long)"receive", (char*)id_str) == 0)) { + tok_type = 36LL; + } + if ((strcmp((char*)(long long)"from", (char*)id_str) == 0)) { + tok_type = 37LL; + } + if ((strcmp((char*)(long long)"external", (char*)id_str) == 0)) { + tok_type = 38LL; + } + if ((strcmp((char*)(long long)"borrow", (char*)id_str) == 0)) { + tok_type = 39LL; + } + if ((strcmp((char*)(long long)"structure", (char*)id_str) == 0)) { + tok_type = 44LL; + } + if ((strcmp((char*)(long long)"field", (char*)id_str) == 0)) { + tok_type = 45LL; + } + if ((strcmp((char*)(long long)"create", (char*)id_str) == 0)) { + tok_type = 46LL; + } + if ((strcmp((char*)(long long)"as", (char*)id_str) == 0)) { + tok_type = 42LL; + } + if ((strcmp((char*)(long long)"returning", (char*)id_str) == 0)) { + tok_type = 43LL; + } + if ((strcmp((char*)(long long)"for", (char*)id_str) == 0)) { + tok_type = 53LL; + } + if ((strcmp((char*)(long long)"each", (char*)id_str) == 0)) { + tok_type = 54LL; + } + if ((strcmp((char*)(long long)"in", (char*)id_str) == 0)) { + tok_type = 55LL; + } + if ((strcmp((char*)(long long)"range", (char*)id_str) == 0)) { + tok_type = 60LL; + } + if ((strcmp((char*)(long long)"choice", (char*)id_str) == 0)) { + tok_type = 47LL; + } + if ((strcmp((char*)(long long)"variant", (char*)id_str) == 0)) { + tok_type = 48LL; + } + if ((strcmp((char*)(long long)"check", (char*)id_str) == 0)) { + tok_type = 49LL; + } + if ((strcmp((char*)(long long)"on", (char*)id_str) == 0)) { + tok_type = 56LL; + } + if ((strcmp((char*)(long long)"trait", (char*)id_str) == 0)) { + tok_type = 57LL; + } + if ((strcmp((char*)(long long)"implement", (char*)id_str) == 0)) { + tok_type = 58LL; + } + if ((strcmp((char*)(long long)"not", (char*)id_str) == 0)) { + tok_type = 52LL; + } + if ((strcmp((char*)(long long)"break", (char*)id_str) == 0)) { + tok_type = 61LL; + } + if ((strcmp((char*)(long long)"continue", (char*)id_str) == 0)) { + tok_type = 62LL; + } + if ((strcmp((char*)(long long)"of", (char*)id_str) == 0)) { + tok_type = 59LL; + } + if ((strcmp((char*)(long long)"try", (char*)id_str) == 0)) { + tok_type = 51LL; + } + if ((strcmp((char*)(long long)"given", (char*)id_str) == 0)) { + tok_type = 50LL; + } + if ((strcmp((char*)(long long)"true", (char*)id_str) == 0)) { + tok_type = 63LL; + } + if ((strcmp((char*)(long long)"false", (char*)id_str) == 0)) { + tok_type = 64LL; + } + if ((strcmp((char*)(long long)"async", (char*)id_str) == 0)) { + tok_type = 65LL; + } + if ((strcmp((char*)(long long)"await", (char*)id_str) == 0)) { + tok_type = 66LL; + } + if ((strcmp((char*)(long long)"modulo", (char*)id_str) == 0)) { + tok_type = 41LL; + } + if ((strcmp((char*)(long long)"describe", (char*)id_str) == 0)) { + tok_type = 1LL; + } + if ((strcmp((char*)(long long)"let", (char*)id_str) == 0)) { + tok_type = 2LL; + } + if ((strcmp((char*)(long long)"be", (char*)id_str) == 0)) { + tok_type = 3LL; + } + if ((strcmp((char*)(long long)"show", (char*)id_str) == 0)) { + tok_type = 7LL; + } + if ((strcmp((char*)(long long)"print", (char*)id_str) == 0)) { + tok_type = 7LL; + } + if ((strcmp((char*)(long long)"loop", (char*)id_str) == 0)) { + tok_type = 8LL; + } + if ((strcmp((char*)(long long)"every", (char*)id_str) == 0)) { + tok_type = 54LL; + } + if ((strcmp((char*)(long long)"returns", (char*)id_str) == 0)) { + tok_type = 43LL; + } + if ((strcmp((char*)(long long)"stop", (char*)id_str) == 0)) { + tok_type = 61LL; + } + if ((strcmp((char*)(long long)"skip", (char*)id_str) == 0)) { + tok_type = 62LL; + } + if ((strcmp((char*)(long long)"times", (char*)id_str) == 0)) { + tok_type = 14LL; + } + } + tok = (create_token(tok_type, id_str, current_line, (current_col - id_len)) + 0LL); + ok = append_list(tokens, tok); + } else { + if (c == 34LL) { + start_col = current_col; + is_fstring = 0LL; + tok_count = length_list(tokens); + if (tok_count > 0LL) { + last_tok = get_list(tokens, (tok_count - 1LL)); + if (get_token_type(last_tok) == 27LL) { + if ((strcmp((char*)(long long)"f", (char*)get_token_value(last_tok)) == 0)) { + is_fstring = 1LL; + popped = pop_list(tokens); + } + } + } + sres = (lex_string_body(source, pos, source_len, tokens, current_line, start_col, is_fstring) + 0LL); + pos = get_list(sres, 0LL); + current_col = get_list(sres, 1LL); + if (get_list(sres, 2LL) == 1LL) { + ret_val = 0LL; + goto L_cleanup; + } + } else { + sym_type = 0LL; + sym_val = (long long)""; + sym_len = 1LL; + next_c = get_character((char*)source, (pos + 1LL)); + if ((c == 61LL && next_c == 61LL)) { + sym_type = 18LL; + sym_val = (long long)"=="; + sym_len = 2LL; + } + if ((c == 33LL && next_c == 61LL)) { + sym_type = 19LL; + sym_val = (long long)"!="; + sym_len = 2LL; + } + if ((c == 38LL && next_c == 38LL)) { + sym_type = 20LL; + sym_val = (long long)"&&"; + sym_len = 2LL; + } + if ((c == 124LL && next_c == 124LL)) { + sym_type = 21LL; + sym_val = (long long)"||"; + sym_len = 2LL; + } + if ((c == 60LL && next_c == 61LL)) { + sym_type = 67LL; + sym_val = (long long)"<="; + sym_len = 2LL; + } + if ((c == 62LL && next_c == 61LL)) { + sym_type = 68LL; + sym_val = (long long)">="; + sym_len = 2LL; + } + if (sym_type == 0LL) { + if (c == 58LL) { + sym_type = 22LL; + sym_val = (long long)":"; + } + if (c == 40LL) { + sym_type = 23LL; + sym_val = (long long)"("; + } + if (c == 41LL) { + sym_type = 24LL; + sym_val = (long long)")"; + } + if (c == 43LL) { + sym_type = 12LL; + sym_val = (long long)"+"; + } + if (c == 45LL) { + sym_type = 13LL; + sym_val = (long long)"-"; + } + if (c == 42LL) { + sym_type = 14LL; + sym_val = (long long)"*"; + } + if (c == 47LL) { + sym_type = 15LL; + sym_val = (long long)"/"; + } + if (c == 60LL) { + sym_type = 16LL; + sym_val = (long long)"<"; + } + if (c == 62LL) { + sym_type = 17LL; + sym_val = (long long)">"; + } + if (c == 46LL) { + sym_type = 40LL; + sym_val = (long long)"."; + } + if (c == 37LL) { + sym_type = 41LL; + sym_val = (long long)"%"; + } + if (c == 91LL) { + sym_type = 69LL; + sym_val = (long long)"["; + } + if (c == 93LL) { + sym_type = 70LL; + sym_val = (long long)"]"; + } + if (c == 44LL) { + sym_type = 71LL; + sym_val = (long long)","; + } + } + if (sym_type > 0LL) { + tok = (create_token(sym_type, sym_val, current_line, current_col) + 0LL); + ok = append_list(tokens, tok); + pos = (pos + sym_len); + current_col = (current_col + sym_len); + } else { + printf("%s\n", (char*)(long long)"Lexer Error: Unknown symbol character code:"); + printf("%lld\n", c); + pos = (pos + 1LL); + current_col = (current_col + 1LL); + } + } + } + } + } + } + } + } + } + stack_len = length_list(indent_stack); + while (stack_len > 1LL) { + tok = (create_token(30LL, (long long)"DEDENT", current_line, current_col) + 0LL); + ok = append_list(tokens, tok); + popped = pop_list(indent_stack); + stack_len = (stack_len - 1LL); + } + tok = (create_token(31LL, (long long)"EOF", current_line, current_col) + 0LL); + ok = append_list(tokens, tok); + ret_val = tokens; + tokens = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(3); + free_list(tokens); + free_list(indent_stack); + return ret_val; +} + +long long parse_int(long long s) { + long long val = 0; + long long len = 0; + long long idx = 0; + long long ch = 0; + long long digit = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + val = 0LL; + len = string_length((char*)s); + idx = 0LL; + while (idx < len) { + ch = get_character((char*)s, idx); + digit = (ch - 48LL); + val = ((val * 10LL) + digit); + idx = (idx + 1LL); + } + ret_val = val; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long make_node_int(long long val) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 1LL); + ok = append_list(node, val); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_str(long long val) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 2LL); + ok = append_list(node, val); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_ident(long long name) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 3LL); + ok = append_list(node, name); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_binary(long long left, long long op, long long right) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 4LL); + ok = append_list(node, left); + ok = append_list(node, op); + ok = append_list(node, right); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_comp(long long left, long long op, long long right) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 5LL); + ok = append_list(node, left); + ok = append_list(node, op); + ok = append_list(node, right); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_call(long long name, long long args) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 6LL); + ok = append_list(node, name); + ok = append_list(node, args); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_set(long long var, long long expr) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 7LL); + ok = append_list(node, var); + ok = append_list(node, expr); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_return(long long expr) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 8LL); + ok = append_list(node, expr); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_display(long long expr) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 9LL); + ok = append_list(node, expr); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_if(long long cond, long long then_b, long long else_b) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 10LL); + ok = append_list(node, cond); + ok = append_list(node, then_b); + ok = append_list(node, else_b); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_repeat_while(long long cond, long long body) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 11LL); + ok = append_list(node, cond); + ok = append_list(node, body); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_func(long long name, long long params, long long body, long long is_async) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 12LL); + ok = append_list(node, name); + ok = append_list(node, params); + ok = append_list(node, body); + ok = append_list(node, is_async); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_program(long long imports, long long externals, long long funcs, long long struct_defs, long long enum_defs, long long method_defs, long long trait_defs, long long trait_impls) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 13LL); + ok = append_list(node, imports); + ok = append_list(node, externals); + ok = append_list(node, funcs); + ok = append_list(node, struct_defs); + ok = append_list(node, enum_defs); + ok = append_list(node, method_defs); + ok = append_list(node, trait_defs); + ok = append_list(node, trait_impls); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_spawn(long long func_name, long long args) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 15LL); + ok = append_list(node, func_name); + ok = append_list(node, args); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_send(long long chan, long long val) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 16LL); + ok = append_list(node, chan); + ok = append_list(node, val); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_channel() { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 17LL); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_receive(long long chan) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 18LL); + ok = append_list(node, chan); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_external(long long name, long long params, long long ret_type) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 19LL); + ok = append_list(node, name); + ok = append_list(node, params); + ok = append_list(node, ret_type); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_borrow(long long target) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 20LL); + ok = append_list(node, target); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_await(long long target) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 21LL); + ok = append_list(node, target); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_logical(long long left, long long op, long long right) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 14LL); + ok = append_list(node, left); + ok = append_list(node, op); + ok = append_list(node, right); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_field_access(long long obj, long long field_name) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 22LL); + ok = append_list(node, obj); + ok = append_list(node, field_name); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_field_set(long long obj, long long field_name, long long val) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 23LL); + ok = append_list(node, obj); + ok = append_list(node, field_name); + ok = append_list(node, val); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_struct_create(long long struct_name, long long fields) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 24LL); + ok = append_list(node, struct_name); + ok = append_list(node, fields); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_method_call(long long obj, long long method_name, long long args) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 25LL); + ok = append_list(node, obj); + ok = append_list(node, method_name); + ok = append_list(node, args); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_enum_create(long long variant_name, long long args) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 26LL); + ok = append_list(node, variant_name); + ok = append_list(node, args); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_match(long long expr, long long arms) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 27LL); + ok = append_list(node, expr); + ok = append_list(node, arms); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_for_each(long long var_name, long long iter_expr, long long body) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 28LL); + ok = append_list(node, var_name); + ok = append_list(node, iter_expr); + ok = append_list(node, body); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_break() { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 29LL); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_continue() { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 30LL); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_bool(long long val) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 31LL); + ok = append_list(node, val); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_unary_not(long long expr) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 32LL); + ok = append_list(node, expr); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_try(long long expr) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 33LL); + ok = append_list(node, expr); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_closure(long long params, long long body) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 34LL); + ok = append_list(node, params); + ok = append_list(node, body); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_list_lit(long long elements) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 35LL); + ok = append_list(node, elements); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_expr_stmt(long long expr) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 36LL); + ok = append_list(node, expr); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_struct_def(long long name, long long fields) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 37LL); + ok = append_list(node, name); + ok = append_list(node, fields); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_enum_def(long long name, long long variants) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 38LL); + ok = append_list(node, name); + ok = append_list(node, variants); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_method_def(long long method_name, long long struct_name, long long params, long long body) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 39LL); + ok = append_list(node, method_name); + ok = append_list(node, struct_name); + ok = append_list(node, params); + ok = append_list(node, body); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_trait_def(long long name, long long method_sigs) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 40LL); + ok = append_list(node, name); + ok = append_list(node, method_sigs); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long make_node_trait_impl(long long trait_name, long long type_name, long long methods) { + long long node = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&node); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(node); + node = tmp_val; + } + ok = append_list(node, 41LL); + ok = append_list(node, trait_name); + ok = append_list(node, type_name); + ok = append_list(node, methods); + ret_val = node; + node = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(node); + return ret_val; +} + +long long create_parser_state(long long tokens) { + long long state = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&state); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(state); + state = tmp_val; + } + ok = append_list(state, tokens); + ok = append_list(state, 0LL); + ok = append_list(state, 0LL); + ret_val = state; + state = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(state); + return ret_val; +} + +long long set_parser_error(long long state) { + long long ok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = set_list(state, 2LL, 1LL); + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_parser_error(long long state) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(state, 2LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_state_tokens(long long state) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(state, 0LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_state_pos(long long state) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(state, 1LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long set_state_pos(long long state, long long new_pos) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = set_list(state, 1LL, new_pos); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_eof_token() { + long long tok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tok = (create_token(31LL, (long long)"EOF", 1LL, 1LL) + 0LL); + ret_val = tok; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long peek_token(long long state) { + long long tokens = 0; + long long pos = 0; + long long tokens_len = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tokens = get_state_tokens(state); + pos = get_state_pos(state); + tokens_len = length_list(tokens); + if (pos < tokens_len) { + ret_val = get_list(tokens, pos); + goto L_cleanup; + } + ret_val = get_eof_token(); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long peek_token_at(long long state, long long offset) { + long long tokens = 0; + long long pos = 0; + long long tokens_len = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tokens = get_state_tokens(state); + pos = (get_state_pos(state) + offset); + tokens_len = length_list(tokens); + if (pos < tokens_len) { + ret_val = get_list(tokens, pos); + goto L_cleanup; + } + ret_val = get_eof_token(); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long advance_token(long long state) { + long long tokens = 0; + long long pos = 0; + long long tokens_len = 0; + long long tok = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tokens = get_state_tokens(state); + pos = get_state_pos(state); + tokens_len = length_list(tokens); + if (pos < tokens_len) { + tok = get_list(tokens, pos); + ok = set_state_pos(state, (pos + 1LL)); + ret_val = tok; + goto L_cleanup; + } + ret_val = get_eof_token(); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long expect_token_type(long long state, long long expected_type) { + long long tok = 0; + long long actual_type = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tok = advance_token(state); + actual_type = get_token_type(tok); + if (actual_type == expected_type) { + ret_val = 1LL; + goto L_cleanup; + } + printf("%s\n", (char*)(long long)"Parser Error: Expected token type:"); + printf("%lld\n", expected_type); + printf("%s\n", (char*)(long long)"Found type:"); + printf("%lld\n", actual_type); + printf("%s\n", (char*)(long long)"At line:"); + printf("%lld\n", get_token_line(tok)); + ok = set_parser_error(state); + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_token_precedence(long long tok) { + long long t = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + t = get_token_type(tok); + if (t == 21LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (t == 20LL) { + ret_val = 2LL; + goto L_cleanup; + } + if ((((((t == 16LL || t == 17LL) || t == 18LL) || t == 19LL) || t == 67LL) || t == 68LL)) { + ret_val = 3LL; + goto L_cleanup; + } + if ((t == 12LL || t == 13LL)) { + ret_val = 4LL; + goto L_cleanup; + } + if (((t == 14LL || t == 15LL) || t == 41LL)) { + ret_val = 5LL; + goto L_cleanup; + } + if (t == 23LL) { + ret_val = 6LL; + goto L_cleanup; + } + if (t == 40LL) { + ret_val = 7LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long skip_newlines(long long state) { + long long loop = 0; + long long tok = 0; + long long dummy = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + loop = 1LL; + while (loop == 1LL) { + tok = peek_token(state); + if (get_token_type(tok) == 28LL) { + dummy = advance_token(state); + } else { + loop = 0LL; + } + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long is_uppercase_start(long long name) { + long long ch = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ch = get_character((char*)name, 0LL); + if ((ch > 64LL && ch < 91LL)) { + ret_val = 1LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_param_list(long long state) { + long long params = 0; + long long next_tok = 0; + long long dummy = 0; + long long next_tok2 = 0; + long long is_borrow = 0; + long long tok_param = 0; + long long param_name = 0; + long long next_as = 0; + long long param_node = 0; + long long ok = 0; + long long loop = 0; + long long tok_and = 0; + long long next_tok3 = 0; + long long is_borrow3 = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + params = (create_list() + 0LL); + next_tok = peek_token(state); + if (get_token_type(next_tok) == 10LL) { + dummy = advance_token(state); + next_tok2 = peek_token(state); + is_borrow = 0LL; + if (get_token_type(next_tok2) == 39LL) { + dummy = advance_token(state); + is_borrow = 1LL; + } + tok_param = advance_token(state); + param_name = get_token_value(tok_param); + next_as = peek_token(state); + if (get_token_type(next_as) == 42LL) { + dummy = advance_token(state); + dummy = advance_token(state); + } + param_node = (create_list() + 0LL); + ok = append_list(param_node, param_name); + ok = append_list(param_node, is_borrow); + ok = append_list(params, param_node); + loop = 1LL; + while (loop == 1LL) { + tok_and = peek_token(state); + if (get_token_type(tok_and) == 11LL) { + dummy = advance_token(state); + next_tok3 = peek_token(state); + is_borrow3 = 0LL; + if (get_token_type(next_tok3) == 39LL) { + dummy = advance_token(state); + is_borrow3 = 1LL; + } + tok_param = advance_token(state); + param_name = get_token_value(tok_param); + next_as = peek_token(state); + if (get_token_type(next_as) == 42LL) { + dummy = advance_token(state); + dummy = advance_token(state); + } + param_node = (create_list() + 0LL); + ok = append_list(param_node, param_name); + ok = append_list(param_node, is_borrow3); + ok = append_list(params, param_node); + } else { + loop = 0LL; + } + } + } + ret_val = params; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_program(long long state) { + long long imports = 0; + long long externals = 0; + long long funcs = 0; + long long struct_defs = 0; + long long enum_defs = 0; + long long method_defs = 0; + long long trait_defs = 0; + long long trait_impls = 0; + long long loop = 0; + long long tok = 0; + long long t = 0; + long long dummy = 0; + long long tok_path = 0; + long long path_type = 0; + long long path_val = 0; + long long alias_val = 0; + long long peek_as = 0; + long long dummy2 = 0; + long long tok_alias = 0; + long long imp_pair = 0; + long long ok = 0; + long long tok_name = 0; + long long name = 0; + long long params = 0; + long long ret_type = 0; + long long next_tok = 0; + long long ret_tok = 0; + long long ext_node = 0; + long long func = 0; + long long tok2 = 0; + long long t2 = 0; + long long struct_def = 0; + long long enum_def = 0; + long long trait_def = 0; + long long tok3 = 0; + long long t3 = 0; + long long method_def = 0; + long long impl_node = 0; + long long prog = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + imports = (create_list() + 0LL); + externals = (create_list() + 0LL); + funcs = (create_list() + 0LL); + struct_defs = (create_list() + 0LL); + enum_defs = (create_list() + 0LL); + method_defs = (create_list() + 0LL); + trait_defs = (create_list() + 0LL); + trait_impls = (create_list() + 0LL); + loop = 1LL; + while (loop == 1LL) { + tok = peek_token(state); + t = get_token_type(tok); + if (t == 31LL) { + loop = 0LL; + } else { + if (t == 28LL) { + dummy = advance_token(state); + } else { + if (t == 32LL) { + dummy = advance_token(state); + tok_path = advance_token(state); + path_type = get_token_type(tok_path); + if (path_type == 26LL) { + path_val = get_token_value(tok_path); + alias_val = (long long)""; + peek_as = peek_token(state); + if (get_token_type(peek_as) == 42LL) { + dummy2 = advance_token(state); + tok_alias = advance_token(state); + alias_val = get_token_value(tok_alias); + } + imp_pair = (create_list() + 0LL); + ok = append_list(imp_pair, path_val); + ok = append_list(imp_pair, alias_val); + ok = append_list(imports, imp_pair); + } else { + printf("%s\n", (char*)(long long)"Parser Error: Expected string literal after import at line:"); + printf("%lld\n", get_token_line(tok_path)); + loop = 0LL; + } + } else { + if (t == 38LL) { + dummy = advance_token(state); + ok = expect_token_type(state, 1LL); + tok_name = advance_token(state); + name = get_token_value(tok_name); + params = (parse_param_list(state) + 0LL); + ret_type = (long long)""; + next_tok = peek_token(state); + if (get_token_type(next_tok) == 43LL) { + dummy = advance_token(state); + ret_tok = advance_token(state); + ret_type = get_token_value(ret_tok); + } + next_tok = peek_token(state); + if (get_token_type(next_tok) == 22LL) { + dummy = advance_token(state); + } + next_tok = peek_token(state); + if (get_token_type(next_tok) == 28LL) { + dummy = advance_token(state); + } + ext_node = (make_node_external(name, params, ret_type) + 0LL); + ok = append_list(externals, ext_node); + } else { + if (t == 65LL) { + dummy = advance_token(state); + func = (parse_function_async(state, 1LL) + 0LL); + ok = append_list(funcs, func); + } else { + if (t == 1LL) { + tok2 = peek_token_at(state, 1LL); + t2 = get_token_type(tok2); + if (t2 == 44LL) { + struct_def = (parse_struct_def(state) + 0LL); + ok = append_list(struct_defs, struct_def); + } else { + if (t2 == 47LL) { + enum_def = (parse_enum_def(state) + 0LL); + ok = append_list(enum_defs, enum_def); + } else { + if (t2 == 57LL) { + trait_def = (parse_trait_def(state) + 0LL); + ok = append_list(trait_defs, trait_def); + } else { + tok3 = peek_token_at(state, 2LL); + t3 = get_token_type(tok3); + if (t3 == 56LL) { + method_def = (parse_method_def(state) + 0LL); + ok = append_list(method_defs, method_def); + } else { + func = (parse_function_async(state, 0LL) + 0LL); + ok = append_list(funcs, func); + } + } + } + } + } else { + if (t == 58LL) { + impl_node = (parse_trait_impl(state) + 0LL); + ok = append_list(trait_impls, impl_node); + } else { + printf("%s\n", (char*)(long long)"Parser Error: Unexpected token at top level:"); + printf("%lld\n", t); + ok = set_parser_error(state); + loop = 0LL; + } + } + } + } + } + } + } + } + prog = (make_node_program(imports, externals, funcs, struct_defs, enum_defs, method_defs, trait_defs, trait_impls) + 0LL); + ret_val = prog; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_struct_def(long long state) { + long long ok = 0; + long long tok_name = 0; + long long name = 0; + long long fields = 0; + long long field_loop = 0; + long long tok = 0; + long long t = 0; + long long dummy = 0; + long long field_name_tok = 0; + long long field_name = 0; + long long field_type = 0; + long long field_default = 0; + long long next = 0; + long long type_tok = 0; + long long field_node = 0; + long long tok_ded = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = expect_token_type(state, 1LL); + ok = expect_token_type(state, 44LL); + tok_name = advance_token(state); + name = get_token_value(tok_name); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + ok = expect_token_type(state, 29LL); + fields = (create_list() + 0LL); + field_loop = 1LL; + while (field_loop == 1LL) { + tok = peek_token(state); + t = get_token_type(tok); + if ((t == 30LL || t == 31LL)) { + field_loop = 0LL; + } else { + if (t == 28LL) { + dummy = advance_token(state); + } else { + if (t == 45LL) { + dummy = advance_token(state); + field_name_tok = advance_token(state); + field_name = get_token_value(field_name_tok); + field_type = (long long)"Int"; + field_default = 0LL; + next = peek_token(state); + if (get_token_type(next) == 42LL) { + dummy = advance_token(state); + type_tok = advance_token(state); + field_type = get_token_value(type_tok); + } + next = peek_token(state); + if (get_token_type(next) == 72LL) { + dummy = advance_token(state); + field_default = (parse_expr(state, 0LL) + 0LL); + } + field_node = (create_list() + 0LL); + ok = append_list(field_node, field_name); + ok = append_list(field_node, field_type); + ok = append_list(field_node, field_default); + ok = append_list(fields, field_node); + } else { + dummy = advance_token(state); + } + } + } + } + tok_ded = peek_token(state); + if (get_token_type(tok_ded) == 30LL) { + dummy = advance_token(state); + } + ret_val = make_node_struct_def(name, fields); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_enum_def(long long state) { + long long ok = 0; + long long tok_name = 0; + long long name = 0; + long long variants = 0; + long long var_loop = 0; + long long tok = 0; + long long t = 0; + long long dummy = 0; + long long vname_tok = 0; + long long vname = 0; + long long vfields = 0; + long long next = 0; + long long vf_tok = 0; + long long vf_name = 0; + long long vf_type = 0; + long long next_as = 0; + long long vt_tok = 0; + long long vf_node = 0; + long long vf_loop = 0; + long long next_and = 0; + long long v_node = 0; + long long tok_ded = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = expect_token_type(state, 1LL); + ok = expect_token_type(state, 47LL); + tok_name = advance_token(state); + name = get_token_value(tok_name); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + ok = expect_token_type(state, 29LL); + variants = (create_list() + 0LL); + var_loop = 1LL; + while (var_loop == 1LL) { + tok = peek_token(state); + t = get_token_type(tok); + if ((t == 30LL || t == 31LL)) { + var_loop = 0LL; + } else { + if (t == 28LL) { + dummy = advance_token(state); + } else { + if (t == 48LL) { + dummy = advance_token(state); + vname_tok = advance_token(state); + vname = get_token_value(vname_tok); + vfields = (create_list() + 0LL); + next = peek_token(state); + if (get_token_type(next) == 10LL) { + dummy = advance_token(state); + vf_tok = advance_token(state); + vf_name = get_token_value(vf_tok); + vf_type = (long long)"Int"; + next_as = peek_token(state); + if (get_token_type(next_as) == 42LL) { + dummy = advance_token(state); + vt_tok = advance_token(state); + vf_type = get_token_value(vt_tok); + } + vf_node = (create_list() + 0LL); + ok = append_list(vf_node, vf_name); + ok = append_list(vf_node, vf_type); + ok = append_list(vfields, vf_node); + vf_loop = 1LL; + while (vf_loop == 1LL) { + next_and = peek_token(state); + if (get_token_type(next_and) == 11LL) { + dummy = advance_token(state); + vf_tok = advance_token(state); + vf_name = get_token_value(vf_tok); + vf_type = (long long)"Int"; + next_as = peek_token(state); + if (get_token_type(next_as) == 42LL) { + dummy = advance_token(state); + vt_tok = advance_token(state); + vf_type = get_token_value(vt_tok); + } + vf_node = (create_list() + 0LL); + ok = append_list(vf_node, vf_name); + ok = append_list(vf_node, vf_type); + ok = append_list(vfields, vf_node); + } else { + vf_loop = 0LL; + } + } + } + v_node = (create_list() + 0LL); + ok = append_list(v_node, vname); + ok = append_list(v_node, vfields); + ok = append_list(variants, v_node); + } else { + dummy = advance_token(state); + } + } + } + } + tok_ded = peek_token(state); + if (get_token_type(tok_ded) == 30LL) { + dummy = advance_token(state); + } + ret_val = make_node_enum_def(name, variants); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_method_def(long long state) { + long long ok = 0; + long long tok_method = 0; + long long method_name = 0; + long long tok_struct = 0; + long long struct_name = 0; + long long params = 0; + long long next_ret = 0; + long long dummy = 0; + long long body = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = expect_token_type(state, 1LL); + tok_method = advance_token(state); + method_name = get_token_value(tok_method); + ok = expect_token_type(state, 56LL); + tok_struct = advance_token(state); + struct_name = get_token_value(tok_struct); + params = (parse_param_list(state) + 0LL); + next_ret = peek_token(state); + if (get_token_type(next_ret) == 43LL) { + dummy = advance_token(state); + dummy = advance_token(state); + } + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + body = (parse_block(state) + 0LL); + ret_val = make_node_method_def(method_name, struct_name, params, body); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_trait_def(long long state) { + long long ok = 0; + long long tok_name = 0; + long long name = 0; + long long method_sigs = 0; + long long t_loop = 0; + long long tok = 0; + long long t = 0; + long long dummy = 0; + long long sig_tok = 0; + long long sig_name = 0; + long long sig_params = 0; + long long next_ret = 0; + long long next = 0; + long long sig_node = 0; + long long tok_ded = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = expect_token_type(state, 1LL); + ok = expect_token_type(state, 57LL); + tok_name = advance_token(state); + name = get_token_value(tok_name); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + ok = expect_token_type(state, 29LL); + method_sigs = (create_list() + 0LL); + t_loop = 1LL; + while (t_loop == 1LL) { + tok = peek_token(state); + t = get_token_type(tok); + if ((t == 30LL || t == 31LL)) { + t_loop = 0LL; + } else { + if (t == 28LL) { + dummy = advance_token(state); + } else { + if (t == 1LL) { + dummy = advance_token(state); + sig_tok = advance_token(state); + sig_name = get_token_value(sig_tok); + sig_params = (parse_param_list(state) + 0LL); + next_ret = peek_token(state); + if (get_token_type(next_ret) == 43LL) { + dummy = advance_token(state); + dummy = advance_token(state); + } + next = peek_token(state); + if (get_token_type(next) == 22LL) { + dummy = advance_token(state); + } + sig_node = (create_list() + 0LL); + ok = append_list(sig_node, sig_name); + ok = append_list(sig_node, sig_params); + ok = append_list(method_sigs, sig_node); + } else { + dummy = advance_token(state); + } + } + } + } + tok_ded = peek_token(state); + if (get_token_type(tok_ded) == 30LL) { + dummy = advance_token(state); + } + ret_val = make_node_trait_def(name, method_sigs); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_trait_impl(long long state) { + long long ok = 0; + long long tok_trait = 0; + long long trait_name = 0; + long long tok_type = 0; + long long type_name = 0; + long long methods = 0; + long long impl_loop = 0; + long long tok = 0; + long long t = 0; + long long dummy = 0; + long long method = 0; + long long tok_ded = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = expect_token_type(state, 58LL); + tok_trait = advance_token(state); + trait_name = get_token_value(tok_trait); + ok = expect_token_type(state, 53LL); + tok_type = advance_token(state); + type_name = get_token_value(tok_type); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + ok = expect_token_type(state, 29LL); + methods = (create_list() + 0LL); + impl_loop = 1LL; + while (impl_loop == 1LL) { + tok = peek_token(state); + t = get_token_type(tok); + if ((t == 30LL || t == 31LL)) { + impl_loop = 0LL; + } else { + if (t == 28LL) { + dummy = advance_token(state); + } else { + if (t == 1LL) { + method = (parse_function_async(state, 0LL) + 0LL); + ok = append_list(methods, method); + } else { + dummy = advance_token(state); + } + } + } + } + tok_ded = peek_token(state); + if (get_token_type(tok_ded) == 30LL) { + dummy = advance_token(state); + } + ret_val = make_node_trait_impl(trait_name, type_name, methods); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_function_async(long long state, long long is_async) { + long long ok = 0; + long long tok_name = 0; + long long name = 0; + long long params = 0; + long long next_ret = 0; + long long dummy = 0; + long long tok_nl = 0; + long long body = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = expect_token_type(state, 1LL); + tok_name = advance_token(state); + name = get_token_value(tok_name); + params = (parse_param_list(state) + 0LL); + next_ret = peek_token(state); + if (get_token_type(next_ret) == 43LL) { + dummy = advance_token(state); + dummy = advance_token(state); + } + ok = expect_token_type(state, 22LL); + tok_nl = peek_token(state); + if (get_token_type(tok_nl) == 28LL) { + dummy = advance_token(state); + } + body = (parse_block(state) + 0LL); + ret_val = make_node_func(name, params, body, is_async); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_block(long long state) { + long long ok = 0; + long long statements = 0; + long long loop = 0; + long long tok = 0; + long long t = 0; + long long dummy = 0; + long long stmt = 0; + long long tok_dedent = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ok = expect_token_type(state, 29LL); + statements = (create_list() + 0LL); + loop = 1LL; + while (loop == 1LL) { + tok = peek_token(state); + t = get_token_type(tok); + if ((t == 30LL || t == 31LL)) { + loop = 0LL; + } else { + if (t == 28LL) { + dummy = advance_token(state); + } else { + stmt = (parse_statement(state) + 0LL); + ok = append_list(statements, stmt); + } + } + } + tok_dedent = peek_token(state); + if (get_token_type(tok_dedent) == 30LL) { + dummy = advance_token(state); + } else { + printf("%s\n", (char*)(long long)"Parser Error: Expected DEDENT, found:"); + printf("%lld\n", get_token_type(tok_dedent)); + } + ret_val = statements; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_statement(long long state) { + long long tok = 0; + long long t = 0; + long long dummy = 0; + long long tok_var = 0; + long long var_name = 0; + long long next = 0; + long long field_tok = 0; + long long field_name = 0; + long long ok = 0; + long long val = 0; + long long obj = 0; + long long expr = 0; + long long next_as = 0; + long long cond = 0; + long long body = 0; + long long tok_func = 0; + long long func_name = 0; + long long args = 0; + long long next_tok = 0; + long long arg = 0; + long long loop_args = 0; + long long next_tok3 = 0; + long long chan = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tok = peek_token(state); + t = get_token_type(tok); + if (t == 2LL) { + dummy = advance_token(state); + tok_var = advance_token(state); + var_name = get_token_value(tok_var); + next = peek_token(state); + if (get_token_type(next) == 40LL) { + dummy = advance_token(state); + field_tok = advance_token(state); + field_name = get_token_value(field_tok); + ok = expect_token_type(state, 3LL); + val = (parse_expr(state, 0LL) + 0LL); + obj = (make_node_ident(var_name) + 0LL); + ok = skip_newlines(state); + ret_val = make_node_field_set(obj, field_name, val); + goto L_cleanup; + } else { + ok = expect_token_type(state, 3LL); + expr = (parse_expr(state, 0LL) + 0LL); + next_as = peek_token(state); + if (get_token_type(next_as) == 42LL) { + dummy = advance_token(state); + dummy = advance_token(state); + } + ok = skip_newlines(state); + ret_val = make_node_set(var_name, expr); + goto L_cleanup; + } + } else { + if (t == 4LL) { + ret_val = parse_if_statement(state); + goto L_cleanup; + } else { + if ((t == 8LL || t == 9LL)) { + dummy = advance_token(state); + if (t == 8LL) { + ok = expect_token_type(state, 9LL); + } + cond = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + body = (parse_block(state) + 0LL); + ret_val = make_node_repeat_while(cond, body); + goto L_cleanup; + } else { + if (t == 6LL) { + dummy = advance_token(state); + expr = (parse_expr(state, 0LL) + 0LL); + ok = skip_newlines(state); + ret_val = make_node_return(expr); + goto L_cleanup; + } else { + if (t == 7LL) { + dummy = advance_token(state); + expr = (parse_expr(state, 0LL) + 0LL); + ok = skip_newlines(state); + ret_val = make_node_display(expr); + goto L_cleanup; + } else { + if (t == 33LL) { + dummy = advance_token(state); + tok_func = advance_token(state); + func_name = get_token_value(tok_func); + ok = expect_token_type(state, 23LL); + args = (create_list() + 0LL); + next_tok = peek_token(state); + if (get_token_type(next_tok) != 24LL) { + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + loop_args = 1LL; + while (loop_args == 1LL) { + next_tok3 = peek_token(state); + if (get_token_type(next_tok3) == 11LL) { + dummy = advance_token(state); + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + } else { + loop_args = 0LL; + } + } + } + ok = expect_token_type(state, 24LL); + ok = skip_newlines(state); + ret_val = make_node_spawn(func_name, args); + goto L_cleanup; + } else { + if (t == 35LL) { + dummy = advance_token(state); + val = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 3LL); + chan = (parse_expr(state, 0LL) + 0LL); + ok = skip_newlines(state); + ret_val = make_node_send(chan, val); + goto L_cleanup; + } else { + if (t == 49LL) { + ret_val = parse_match_statement(state); + goto L_cleanup; + } else { + if (t == 53LL) { + ret_val = parse_for_each_statement(state); + goto L_cleanup; + } else { + if (t == 61LL) { + dummy = advance_token(state); + ok = skip_newlines(state); + ret_val = make_node_break(); + goto L_cleanup; + } else { + if (t == 62LL) { + dummy = advance_token(state); + ok = skip_newlines(state); + ret_val = make_node_continue(); + goto L_cleanup; + } else { + expr = (parse_expr(state, 0LL) + 0LL); + ok = skip_newlines(state); + ret_val = make_node_expr_stmt(expr); + goto L_cleanup; + } + } + } + } + } + } + } + } + } + } + } +L_cleanup: + return ret_val; +} + +long long parse_if_statement(long long state) { + long long dummy = 0; + long long cond = 0; + long long ok = 0; + long long then_branch = 0; + long long else_branch = 0; + long long next = 0; + long long next2 = 0; + long long chained_if = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + dummy = advance_token(state); + cond = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + then_branch = (parse_block(state) + 0LL); + else_branch = 0LL; + next = peek_token(state); + if (get_token_type(next) == 5LL) { + dummy = advance_token(state); + next2 = peek_token(state); + if (get_token_type(next2) == 4LL) { + chained_if = (parse_if_statement(state) + 0LL); + else_branch = (create_list() + 0LL); + ok = append_list(else_branch, chained_if); + } else { + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + else_branch = (parse_block(state) + 0LL); + } + } + ret_val = make_node_if(cond, then_branch, else_branch); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_match_statement(long long state) { + long long dummy = 0; + long long expr = 0; + long long ok = 0; + long long arms = 0; + long long arm_loop = 0; + long long tok = 0; + long long tok_t = 0; + long long variant_tok = 0; + long long variant_name = 0; + long long bindings = 0; + long long next = 0; + long long bind_tok = 0; + long long b_loop = 0; + long long next_and = 0; + long long arm_body = 0; + long long arm_node = 0; + long long tok_ded = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + dummy = advance_token(state); + expr = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + ok = expect_token_type(state, 29LL); + arms = (create_list() + 0LL); + arm_loop = 1LL; + while (arm_loop == 1LL) { + tok = peek_token(state); + tok_t = get_token_type(tok); + if ((tok_t == 30LL || tok_t == 31LL)) { + arm_loop = 0LL; + } else { + if (tok_t == 28LL) { + dummy = advance_token(state); + } else { + if (tok_t == 4LL) { + dummy = advance_token(state); + variant_tok = advance_token(state); + variant_name = get_token_value(variant_tok); + bindings = (create_list() + 0LL); + next = peek_token(state); + if (get_token_type(next) == 10LL) { + dummy = advance_token(state); + bind_tok = advance_token(state); + ok = append_list(bindings, get_token_value(bind_tok)); + b_loop = 1LL; + while (b_loop == 1LL) { + next_and = peek_token(state); + if (get_token_type(next_and) == 11LL) { + dummy = advance_token(state); + bind_tok = advance_token(state); + ok = append_list(bindings, get_token_value(bind_tok)); + } else { + b_loop = 0LL; + } + } + } + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + arm_body = (parse_block(state) + 0LL); + arm_node = (create_list() + 0LL); + ok = append_list(arm_node, variant_name); + ok = append_list(arm_node, bindings); + ok = append_list(arm_node, arm_body); + ok = append_list(arms, arm_node); + } else { + dummy = advance_token(state); + } + } + } + } + tok_ded = peek_token(state); + if (get_token_type(tok_ded) == 30LL) { + dummy = advance_token(state); + } + ret_val = make_node_match(expr, arms); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_for_each_statement(long long state) { + long long dummy = 0; + long long ok = 0; + long long var_tok = 0; + long long var_name = 0; + long long iter_expr = 0; + long long body = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + dummy = advance_token(state); + ok = expect_token_type(state, 54LL); + var_tok = advance_token(state); + var_name = get_token_value(var_tok); + ok = expect_token_type(state, 55LL); + iter_expr = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + body = (parse_block(state) + 0LL); + ret_val = make_node_for_each(var_name, iter_expr, body); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_expr(long long state, long long precedence) { + long long left = 0; + long long climbing = 0; + long long next_tok = 0; + long long next_t = 0; + long long next_prec = 0; + long long dummy = 0; + long long member_tok = 0; + long long member_name = 0; + long long next2 = 0; + long long args = 0; + long long next3 = 0; + long long arg = 0; + long long ok = 0; + long long arg_loop = 0; + long long next4 = 0; + long long op_tok = 0; + long long op_type = 0; + long long is_math = 0; + long long op = 0; + long long right_prec = 0; + long long right = 0; + long long is_comp = 0; + long long is_logical = 0; + long long a_loop = 0; + long long left_type = 0; + long long call_name = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + left = (parse_prefix(state) + 0LL); + climbing = 1LL; + while (climbing == 1LL) { + next_tok = peek_token(state); + next_t = get_token_type(next_tok); + next_prec = get_token_precedence(next_tok); + if ((next_t == 40LL && precedence < 7LL)) { + dummy = advance_token(state); + member_tok = advance_token(state); + member_name = get_token_value(member_tok); + next2 = peek_token(state); + if (get_token_type(next2) == 23LL) { + dummy = advance_token(state); + args = (create_list() + 0LL); + next3 = peek_token(state); + if (get_token_type(next3) != 24LL) { + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + arg_loop = 1LL; + while (arg_loop == 1LL) { + next4 = peek_token(state); + if (get_token_type(next4) == 11LL) { + dummy = advance_token(state); + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + } else { + if (get_token_type(next4) == 71LL) { + dummy = advance_token(state); + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + } else { + arg_loop = 0LL; + } + } + } + } + ok = expect_token_type(state, 24LL); + left = (make_node_method_call(left, member_name, args) + 0LL); + } else { + left = (make_node_field_access(left, member_name) + 0LL); + } + } else { + if (precedence < next_prec) { + op_tok = advance_token(state); + op_type = get_token_type(op_tok); + is_math = ((((op_type == 12LL || op_type == 13LL) || op_type == 14LL) || op_type == 15LL) || op_type == 41LL); + if (is_math == 1LL) { + op = 0LL; + if (op_type == 12LL) { + op = 1LL; + } + if (op_type == 13LL) { + op = 2LL; + } + if (op_type == 14LL) { + op = 3LL; + } + if (op_type == 15LL) { + op = 4LL; + } + if (op_type == 41LL) { + op = 5LL; + } + right_prec = get_token_precedence(op_tok); + right = (parse_expr(state, right_prec) + 0LL); + left = (make_node_binary(left, op, right) + 0LL); + } else { + is_comp = (((((op_type == 16LL || op_type == 17LL) || op_type == 18LL) || op_type == 19LL) || op_type == 67LL) || op_type == 68LL); + if (is_comp == 1LL) { + op = 0LL; + if (op_type == 16LL) { + op = 1LL; + } + if (op_type == 17LL) { + op = 2LL; + } + if (op_type == 18LL) { + op = 3LL; + } + if (op_type == 19LL) { + op = 4LL; + } + if (op_type == 67LL) { + op = 5LL; + } + if (op_type == 68LL) { + op = 6LL; + } + right_prec = get_token_precedence(op_tok); + right = (parse_expr(state, right_prec) + 0LL); + left = (make_node_comp(left, op, right) + 0LL); + } else { + is_logical = (op_type == 20LL || op_type == 21LL); + if (is_logical == 1LL) { + op = 0LL; + if (op_type == 20LL) { + op = 1LL; + } + if (op_type == 21LL) { + op = 2LL; + } + right_prec = get_token_precedence(op_tok); + right = (parse_expr(state, right_prec) + 0LL); + left = (make_node_logical(left, op, right) + 0LL); + } else { + if (op_type == 23LL) { + args = (create_list() + 0LL); + next3 = peek_token(state); + if (get_token_type(next3) != 24LL) { + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + a_loop = 1LL; + while (a_loop == 1LL) { + next4 = peek_token(state); + if ((get_token_type(next4) == 11LL || get_token_type(next4) == 71LL)) { + dummy = advance_token(state); + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + } else { + a_loop = 0LL; + } + } + } + ok = expect_token_type(state, 24LL); + left_type = get_list(left, 0LL); + if (left_type == 3LL) { + call_name = get_list(left, 1LL); + left = (make_node_call(call_name, args) + 0LL); + } else { + printf("%s\n", (char*)(long long)"Parser Error: Expected function name before ("); + } + } + } + } + } + } else { + climbing = 0LL; + } + } + } + ret_val = left; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_prefix(long long state) { + long long tok = 0; + long long t = 0; + long long val = 0; + long long ok = 0; + long long chan = 0; + long long target = 0; + long long operand = 0; + long long try_expr = 0; + long long name = 0; + long long next_tok = 0; + long long dummy = 0; + long long args = 0; + long long next_tok2 = 0; + long long arg = 0; + long long loop_args = 0; + long long next_tok3 = 0; + long long nt3_type = 0; + long long next2 = 0; + long long nt2 = 0; + long long vargs = 0; + long long varg = 0; + long long va_loop = 0; + long long next3 = 0; + long long expr = 0; + long long start_expr = 0; + long long end_expr = 0; + long long range_args = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tok = advance_token(state); + t = get_token_type(tok); + if (t == 25LL) { + val = parse_int(get_token_value(tok)); + ret_val = (make_node_int(val) + 0LL); + goto L_cleanup; + } + if (t == 26LL) { + ret_val = (make_node_str(get_token_value(tok)) + 0LL); + goto L_cleanup; + } + if (t == 34LL) { + ret_val = (make_node_channel() + 0LL); + goto L_cleanup; + } + if (t == 36LL) { + ok = expect_token_type(state, 37LL); + chan = (parse_expr(state, 0LL) + 0LL); + ret_val = (make_node_receive(chan) + 0LL); + goto L_cleanup; + } + if (t == 39LL) { + target = (parse_expr(state, 0LL) + 0LL); + ret_val = (make_node_borrow(target) + 0LL); + goto L_cleanup; + } + if (t == 66LL) { + target = (parse_expr(state, 0LL) + 0LL); + ret_val = (make_node_await(target) + 0LL); + goto L_cleanup; + } + if (t == 63LL) { + ret_val = (make_node_bool(1LL) + 0LL); + goto L_cleanup; + } + if (t == 64LL) { + ret_val = (make_node_bool(0LL) + 0LL); + goto L_cleanup; + } + if (t == 52LL) { + operand = (parse_expr(state, 6LL) + 0LL); + ret_val = (make_node_unary_not(operand) + 0LL); + goto L_cleanup; + } + if (t == 51LL) { + try_expr = (parse_expr(state, 0LL) + 0LL); + ret_val = (make_node_try(try_expr) + 0LL); + goto L_cleanup; + } + if (t == 13LL) { + operand = (parse_expr(state, 6LL) + 0LL); + ret_val = (make_node_binary(make_node_int(0LL), 2LL, operand) + 0LL); + goto L_cleanup; + } + if (t == 50LL) { + ret_val = (parse_closure(state) + 0LL); + goto L_cleanup; + } + if (t == 46LL) { + ret_val = (parse_struct_create(state) + 0LL); + goto L_cleanup; + } + if (t == 69LL) { + ret_val = (parse_list_literal(state) + 0LL); + goto L_cleanup; + } + if (((((((((((((((t == 1LL || t == 7LL) || t == 8LL) || t == 43LL) || t == 44LL) || t == 45LL) || t == 47LL) || t == 48LL) || t == 49LL) || t == 54LL) || t == 57LL) || t == 58LL) || t == 60LL) || t == 61LL) || t == 62LL)) { + t = 27LL; + } + if (t == 27LL) { + name = get_token_value(tok); + next_tok = peek_token(state); + if (get_token_type(next_tok) == 23LL) { + dummy = advance_token(state); + args = (create_list() + 0LL); + next_tok2 = peek_token(state); + if (get_token_type(next_tok2) != 24LL) { + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + loop_args = 1LL; + while (loop_args == 1LL) { + next_tok3 = peek_token(state); + nt3_type = get_token_type(next_tok3); + if ((nt3_type == 11LL || nt3_type == 71LL)) { + dummy = advance_token(state); + arg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(args, arg); + } else { + loop_args = 0LL; + } + } + } + ok = expect_token_type(state, 24LL); + ret_val = (make_node_call(name, args) + 0LL); + goto L_cleanup; + } else { + if (is_uppercase_start(name) == 1LL) { + next2 = peek_token(state); + nt2 = get_token_type(next2); + if (nt2 == 10LL) { + dummy = advance_token(state); + vargs = (create_list() + 0LL); + varg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(vargs, varg); + va_loop = 1LL; + while (va_loop == 1LL) { + next3 = peek_token(state); + if (get_token_type(next3) == 11LL) { + dummy = advance_token(state); + varg = (parse_expr(state, 0LL) + 0LL); + ok = append_list(vargs, varg); + } else { + va_loop = 0LL; + } + } + ret_val = (make_node_enum_create(name, vargs) + 0LL); + goto L_cleanup; + } + } + ret_val = (make_node_ident(name) + 0LL); + goto L_cleanup; + } + } + if (t == 23LL) { + expr = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 24LL); + ret_val = expr; + goto L_cleanup; + } + if (t == 60LL) { + ok = expect_token_type(state, 23LL); + start_expr = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 71LL); + end_expr = (parse_expr(state, 0LL) + 0LL); + ok = expect_token_type(state, 24LL); + range_args = (create_list() + 0LL); + ok = append_list(range_args, start_expr); + ok = append_list(range_args, end_expr); + ret_val = (make_node_call((long long)"range", range_args) + 0LL); + goto L_cleanup; + } + printf("%s\n", (char*)(long long)"Parser Error: Expected expression, found token type:"); + printf("%lld\n", t); + ok = set_parser_error(state); + ret_val = (make_node_int(0LL) + 0LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_closure(long long state) { + long long params = 0; + long long next = 0; + long long p_tok = 0; + long long p_name = 0; + long long p_node = 0; + long long ok = 0; + long long p_loop = 0; + long long next_and = 0; + long long dummy = 0; + long long body = 0; + long long single = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + params = (create_list() + 0LL); + next = peek_token(state); + if (get_token_type(next) != 22LL) { + p_tok = advance_token(state); + p_name = get_token_value(p_tok); + p_node = (create_list() + 0LL); + ok = append_list(p_node, p_name); + ok = append_list(p_node, 0LL); + ok = append_list(params, p_node); + p_loop = 1LL; + while (p_loop == 1LL) { + next_and = peek_token(state); + if (get_token_type(next_and) == 11LL) { + dummy = advance_token(state); + p_tok = advance_token(state); + p_name = get_token_value(p_tok); + p_node = (create_list() + 0LL); + ok = append_list(p_node, p_name); + ok = append_list(p_node, 0LL); + ok = append_list(params, p_node); + } else { + p_loop = 0LL; + } + } + } + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + next = peek_token(state); + if (get_token_type(next) == 29LL) { + body = (parse_block(state) + 0LL); + } else { + single = (parse_statement(state) + 0LL); + body = (create_list() + 0LL); + ok = append_list(body, single); + } + ret_val = make_node_closure(params, body); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_struct_create(long long state) { + long long tok_name = 0; + long long struct_name = 0; + long long ok = 0; + long long fields = 0; + long long next = 0; + long long cf_loop = 0; + long long tok = 0; + long long tok_t = 0; + long long dummy = 0; + long long fname_tok = 0; + long long fname = 0; + long long fval = 0; + long long fpair = 0; + long long tok_ded = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + tok_name = advance_token(state); + struct_name = get_token_value(tok_name); + ok = expect_token_type(state, 22LL); + ok = skip_newlines(state); + fields = (create_list() + 0LL); + next = peek_token(state); + if (get_token_type(next) == 29LL) { + ok = expect_token_type(state, 29LL); + cf_loop = 1LL; + while (cf_loop == 1LL) { + tok = peek_token(state); + tok_t = get_token_type(tok); + if ((tok_t == 30LL || tok_t == 31LL)) { + cf_loop = 0LL; + } else { + if (tok_t == 28LL) { + dummy = advance_token(state); + } else { + fname_tok = advance_token(state); + fname = get_token_value(fname_tok); + ok = expect_token_type(state, 72LL); + fval = (parse_expr(state, 0LL) + 0LL); + fpair = (create_list() + 0LL); + ok = append_list(fpair, fname); + ok = append_list(fpair, fval); + ok = append_list(fields, fpair); + } + } + } + tok_ded = peek_token(state); + if (get_token_type(tok_ded) == 30LL) { + dummy = advance_token(state); + } + } + ret_val = make_node_struct_create(struct_name, fields); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long parse_list_literal(long long state) { + long long elements = 0; + long long next = 0; + long long elem = 0; + long long ok = 0; + long long el_loop = 0; + long long nt = 0; + long long dummy = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + elements = (create_list() + 0LL); + next = peek_token(state); + if (get_token_type(next) != 70LL) { + elem = (parse_expr(state, 0LL) + 0LL); + ok = append_list(elements, elem); + el_loop = 1LL; + while (el_loop == 1LL) { + next = peek_token(state); + nt = get_token_type(next); + if (nt == 71LL) { + dummy = advance_token(state); + elem = (parse_expr(state, 0LL) + 0LL); + ok = append_list(elements, elem); + } else { + if (nt == 11LL) { + dummy = advance_token(state); + elem = (parse_expr(state, 0LL) + 0LL); + ok = append_list(elements, elem); + } else { + el_loop = 0LL; + } + } + } + } + ok = expect_token_type(state, 70LL); + ret_val = make_node_list_lit(elements); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long map_get(long long keys, long long values, long long key) { + long long key_str = 0; + long long len = 0; + long long idx = 0; + long long k = 0; + long long ret_val = 0; + + ep_gc_push_root(&key_str); + ep_gc_maybe_collect(); + + key_str = string_concat(key, (long long)""); + len = length_list(keys); + idx = 0LL; + while (idx < len) { + k = get_list(keys, idx); + if ((strcmp((char*)key_str, (char*)k) == 0)) { + ret_val = get_list(values, idx); + goto L_cleanup; + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long map_contains_key(long long keys, long long key) { + long long key_str = 0; + long long len = 0; + long long idx = 0; + long long ret_val = 0; + + ep_gc_push_root(&key_str); + ep_gc_maybe_collect(); + + key_str = string_concat(key, (long long)""); + len = length_list(keys); + idx = 0LL; + while (idx < len) { + if ((strcmp((char*)key_str, (char*)get_list(keys, idx)) == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long collect_idents_expr(long long expr, long long out_names) { + long long t = 0; + long long ok = 0; + long long okl = 0; + long long okr = 0; + long long args = 0; + long long a_len = 0; + long long a_i = 0; + long long oka = 0; + long long fields = 0; + long long f_len = 0; + long long f_i = 0; + long long fpair = 0; + long long okf = 0; + long long okm = 0; + long long margs = 0; + long long m_len = 0; + long long m_i = 0; + long long okma = 0; + long long eargs = 0; + long long e_len = 0; + long long e_i = 0; + long long oke = 0; + long long elems = 0; + long long l_len = 0; + long long l_i = 0; + long long okl2 = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + if (expr == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + t = get_list(expr, 0LL); + if (t == 3LL) { + ok = append_list(out_names, get_list(expr, 1LL)); + ret_val = 0LL; + goto L_cleanup; + } + if (((t == 4LL || t == 5LL) || t == 14LL)) { + okl = collect_idents_expr(get_list(expr, 1LL), out_names); + okr = collect_idents_expr(get_list(expr, 3LL), out_names); + ret_val = 0LL; + goto L_cleanup; + } + if (t == 6LL) { + ok = append_list(out_names, get_list(expr, 1LL)); + args = create_list(); + args = get_list(expr, 2LL); + a_len = length_list(args); + a_i = 0LL; + while (a_i < a_len) { + oka = collect_idents_expr(get_list(args, a_i), out_names); + a_i = (a_i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + if (((((t == 18LL || t == 20LL) || t == 21LL) || t == 32LL) || t == 33LL)) { + ret_val = collect_idents_expr(get_list(expr, 1LL), out_names); + goto L_cleanup; + } + if (t == 22LL) { + ret_val = collect_idents_expr(get_list(expr, 1LL), out_names); + goto L_cleanup; + } + if (t == 24LL) { + fields = get_list(expr, 2LL); + f_len = length_list(fields); + f_i = 0LL; + while (f_i < f_len) { + fpair = get_list(fields, f_i); + okf = collect_idents_expr(get_list(fpair, 1LL), out_names); + f_i = (f_i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + if (t == 25LL) { + okm = collect_idents_expr(get_list(expr, 1LL), out_names); + margs = get_list(expr, 3LL); + m_len = length_list(margs); + m_i = 0LL; + while (m_i < m_len) { + okma = collect_idents_expr(get_list(margs, m_i), out_names); + m_i = (m_i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + if (t == 26LL) { + eargs = get_list(expr, 2LL); + e_len = length_list(eargs); + e_i = 0LL; + while (e_i < e_len) { + oke = collect_idents_expr(get_list(eargs, e_i), out_names); + e_i = (e_i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + if (t == 34LL) { + ret_val = collect_idents_stmts(get_list(expr, 2LL), out_names); + goto L_cleanup; + } + if (t == 35LL) { + elems = get_list(expr, 1LL); + l_len = length_list(elems); + l_i = 0LL; + while (l_i < l_len) { + okl2 = collect_idents_expr(get_list(elems, l_i), out_names); + l_i = (l_i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long collect_idents_stmts(long long stmts, long long out_names) { + long long len = 0; + long long idx = 0; + long long stmt = 0; + long long t = 0; + long long ok7 = 0; + long long ok7b = 0; + long long ok8 = 0; + long long okc = 0; + long long okt = 0; + long long else_b = 0; + long long oke2 = 0; + long long okw = 0; + long long okwb = 0; + long long oks = 0; + long long sargs = 0; + long long s_len = 0; + long long s_i = 0; + long long oksa = 0; + long long okn1 = 0; + long long okn2 = 0; + long long okf1 = 0; + long long okf2 = 0; + long long okm1 = 0; + long long arms = 0; + long long ar_len = 0; + long long ar_i = 0; + long long arm = 0; + long long okab = 0; + long long okfe1 = 0; + long long okfe2 = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = length_list(stmts); + idx = 0LL; + while (idx < len) { + stmt = get_list(stmts, idx); + t = get_list(stmt, 0LL); + if (t == 7LL) { + ok7 = append_list(out_names, get_list(stmt, 1LL)); + ok7b = collect_idents_expr(get_list(stmt, 2LL), out_names); + } + if (((t == 8LL || t == 9LL) || t == 36LL)) { + ok8 = collect_idents_expr(get_list(stmt, 1LL), out_names); + } + if (t == 10LL) { + okc = collect_idents_expr(get_list(stmt, 1LL), out_names); + okt = collect_idents_stmts(get_list(stmt, 2LL), out_names); + else_b = get_list(stmt, 3LL); + if (else_b != 0LL) { + oke2 = collect_idents_stmts(else_b, out_names); + } + } + if (t == 11LL) { + okw = collect_idents_expr(get_list(stmt, 1LL), out_names); + okwb = collect_idents_stmts(get_list(stmt, 2LL), out_names); + } + if (t == 15LL) { + oks = append_list(out_names, get_list(stmt, 1LL)); + sargs = get_list(stmt, 2LL); + s_len = length_list(sargs); + s_i = 0LL; + while (s_i < s_len) { + oksa = collect_idents_expr(get_list(sargs, s_i), out_names); + s_i = (s_i + 1LL); + } + } + if (t == 16LL) { + okn1 = collect_idents_expr(get_list(stmt, 1LL), out_names); + okn2 = collect_idents_expr(get_list(stmt, 2LL), out_names); + } + if (t == 23LL) { + okf1 = collect_idents_expr(get_list(stmt, 1LL), out_names); + okf2 = collect_idents_expr(get_list(stmt, 3LL), out_names); + } + if (t == 27LL) { + okm1 = collect_idents_expr(get_list(stmt, 1LL), out_names); + arms = get_list(stmt, 2LL); + ar_len = length_list(arms); + ar_i = 0LL; + while (ar_i < ar_len) { + arm = get_list(arms, ar_i); + okab = collect_idents_stmts(get_list(arm, 2LL), out_names); + ar_i = (ar_i + 1LL); + } + } + if (t == 28LL) { + okfe1 = collect_idents_expr(get_list(stmt, 2LL), out_names); + okfe2 = collect_idents_stmts(get_list(stmt, 3LL), out_names); + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long map_put(long long keys, long long values, long long key, long long val) { + long long key_str = 0; + long long len = 0; + long long idx = 0; + long long found = 0; + long long k = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&key_str); + ep_gc_maybe_collect(); + + key_str = string_concat(key, (long long)""); + len = length_list(keys); + idx = 0LL; + found = 0LL; + while ((idx < len && found == 0LL)) { + k = get_list(keys, idx); + if ((strcmp((char*)key_str, (char*)k) == 0)) { + ok = set_list(values, idx, val); + found = 1LL; + } + idx = (idx + 1LL); + } + if (found == 0LL) { + ok = append_list(keys, key); + ok = append_list(values, val); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long field_slot_index(long long seen, long long name) { + long long name_str = 0; + long long len = 0; + long long idx = 0; + long long k = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&name_str); + ep_gc_maybe_collect(); + + name_str = string_concat(name, (long long)""); + len = length_list(seen); + idx = 0LL; + while (idx < len) { + k = get_list(seen, idx); + if ((strcmp((char*)name_str, (char*)k) == 0)) { + ret_val = idx; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ok = append_list(seen, name_str); + ret_val = len; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long string_concat(long long s1, long long s2) { + long long lst = 0; + long long len1 = 0; + long long idx = 0; + long long ok = 0; + long long len2 = 0; + long long idx2 = 0; + long long res = 0; + long long ret_val = 0; + + ep_gc_push_root(&lst); + ep_gc_push_root(&res); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lst); + lst = tmp_val; + } + len1 = string_length((char*)s1); + idx = 0LL; + while (idx < len1) { + ok = append_list(lst, get_character((char*)s1, idx)); + idx = (idx + 1LL); + } + len2 = string_length((char*)s2); + idx2 = 0LL; + while (idx2 < len2) { + ok = append_list(lst, get_character((char*)s2, idx2)); + idx2 = (idx2 + 1LL); + } + res = (long long)string_from_list(lst); + ret_val = res; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(2); + free_list(lst); + return ret_val; +} + +long long get_fn_c_name(long long func) { + long long name = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + name = get_list(func, 1LL); + if ((strcmp((char*)(long long)"main", (char*)name) == 0)) { + ret_val = (long long)"_main"; + goto L_cleanup; + } + ret_val = name; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long cg_int_to_str(long long n) { + long long lst = 0; + long long temp = 0; + long long digits = 0; + long long digit = 0; + long long ok = 0; + long long len = 0; + long long d = 0; + long long res = 0; + long long ret_val = 0; + + ep_gc_push_root(&lst); + ep_gc_push_root(&digits); + ep_gc_push_root(&res); + ep_gc_maybe_collect(); + + if (n == 0LL) { + ret_val = (long long)"0"; + goto L_cleanup; + } + { + long long tmp_val = create_list(); + free_list(lst); + lst = tmp_val; + } + temp = n; + { + long long tmp_val = create_list(); + free_list(digits); + digits = tmp_val; + } + while (temp > 0LL) { + digit = (temp - ((temp / 10LL) * 10LL)); + ok = append_list(digits, (digit + 48LL)); + temp = (temp / 10LL); + } + len = length_list(digits); + while (len > 0LL) { + d = pop_list(digits); + ok = append_list(lst, d); + len = (len - 1LL); + } + res = (long long)string_from_list(lst); + ret_val = res; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(3); + free_list(lst); + free_list(digits); + return ret_val; +} + +long long escape_string(long long s) { + long long lst = 0; + long long len = 0; + long long idx = 0; + long long ch = 0; + long long ok = 0; + long long res = 0; + long long ret_val = 0; + + ep_gc_push_root(&lst); + ep_gc_push_root(&res); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lst); + lst = tmp_val; + } + len = string_length((char*)s); + idx = 0LL; + while (idx < len) { + ch = get_character((char*)s, idx); + if (ch == 92LL) { + ok = append_list(lst, 92LL); + ok = append_list(lst, 92LL); + } else { + if (ch == 34LL) { + ok = append_list(lst, 92LL); + ok = append_list(lst, 34LL); + } else { + if (ch == 10LL) { + ok = append_list(lst, 92LL); + ok = append_list(lst, 110LL); + } else { + if (ch == 9LL) { + ok = append_list(lst, 92LL); + ok = append_list(lst, 116LL); + } else { + if (ch == 13LL) { + ok = append_list(lst, 92LL); + ok = append_list(lst, 114LL); + } else { + ok = append_list(lst, ch); + } + } + } + } + } + idx = (idx + 1LL); + } + res = (long long)string_from_list(lst); + ret_val = res; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(2); + free_list(lst); + return ret_val; +} + +long long join_strings(long long lines) { + long long lst = 0; + long long len = 0; + long long idx = 0; + long long line = 0; + long long line_len = 0; + long long c_idx = 0; + long long ok = 0; + long long res = 0; + long long ret_val = 0; + + ep_gc_push_root(&lst); + ep_gc_push_root(&res); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lst); + lst = tmp_val; + } + len = length_list(lines); + idx = 0LL; + while (idx < len) { + line = get_list(lines, idx); + line_len = string_length((char*)line); + c_idx = 0LL; + while (c_idx < line_len) { + ok = append_list(lst, get_character((char*)line, c_idx)); + c_idx = (c_idx + 1LL); + } + idx = (idx + 1LL); + } + res = (long long)string_from_list(lst); + ret_val = res; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(2); + free_list(lst); + return ret_val; +} + +long long create_codegen_state() { + long long state = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&state); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(state); + state = tmp_val; + } + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, 0LL); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, 0LL); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, 0LL); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, 0LL); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); + ret_val = state; + state = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(state); + return ret_val; +} + +long long is_builtin_c_func(long long state, long long name) { + long long name_str = 0; + long long keys = 0; + long long n = 0; + long long idx = 0; + long long ret_val = 0; + + ep_gc_push_root(&name_str); + ep_gc_maybe_collect(); + + name_str = string_concat(name, (long long)""); + keys = get_list(state, 3LL); + n = get_list(state, 10LL); + idx = 0LL; + while (idx < n) { + if ((strcmp((char*)name_str, (char*)get_list(keys, idx)) == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long get_codegen_borrowed_keys(long long state) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(state, 8LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long set_codegen_borrowed_keys(long long state, long long keys) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = set_list(state, 8LL, keys); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_codegen_borrowed_values(long long state) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(state, 9LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long set_codegen_borrowed_values(long long state, long long values) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = set_list(state, 9LL, values); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_codegen_spawn_list(long long state) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(state, 6LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long set_codegen_spawn_list(long long state, long long spawn_list) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = set_list(state, 6LL, spawn_list); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_codegen_spawn_index(long long state) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_list(state, 7LL); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long set_codegen_spawn_index(long long state, long long spawn_index) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = set_list(state, 7LL, spawn_index); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long emit(long long state, long long line) { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + lines = get_list(state, 0LL); + ok = append_list(lines, line); + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long add_string_literal(long long state, long long s) { + long long s_str = 0; + long long lits = 0; + long long len = 0; + long long idx = 0; + long long val = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&s_str); + ep_gc_maybe_collect(); + + s_str = string_concat(s, (long long)""); + lits = get_list(state, 1LL); + len = length_list(lits); + idx = 0LL; + while (idx < len) { + val = get_list(lits, idx); + if ((strcmp((char*)s_str, (char*)val) == 0)) { + ret_val = idx; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ok = append_list(lits, s); + ret_val = len; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long get_new_label(long long state, long long prefix) { + long long count = 0; + long long next_count = 0; + long long ok = 0; + long long num_str = 0; + long long label_half = 0; + long long label = 0; + long long final_label = 0; + long long ret_val = 0; + + ep_gc_push_root(&label_half); + ep_gc_push_root(&label); + ep_gc_push_root(&final_label); + ep_gc_maybe_collect(); + + count = get_list(state, 2LL); + next_count = (count + 1LL); + ok = set_list(state, 2LL, next_count); + num_str = cg_int_to_str(count); + label_half = string_concat((long long)"L_", prefix); + label = string_concat(label_half, (long long)"_"); + final_label = string_concat(label, num_str); + ret_val = final_label; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(3); + return ret_val; +} + +long long analyze_return_types(long long state, long long program) { + long long keys = 0; + long long values = 0; + long long ok = 0; + long long externals = 0; + long long ext_len = 0; + long long idx = 0; + long long ext_node = 0; + long long ext_name = 0; + long long existing = 0; + long long funcs = 0; + long long funcs_len = 0; + long long pass = 0; + long long func = 0; + long long name = 0; + long long params = 0; + long long body = 0; + long long var_keys = 0; + long long var_values = 0; + long long p_len = 0; + long long p_idx = 0; + long long p_node = 0; + long long p_name = 0; + long long is_borrow = 0; + long long param_type = 0; + long long ret_t = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + keys = get_list(state, 3LL); + values = get_list(state, 4LL); + ok = map_put(keys, values, (long long)"read_file_content", 3LL); + ok = map_put(keys, values, (long long)"create_list", 4LL); + ok = map_put(keys, values, (long long)"ep_md5", 3LL); + ok = map_put(keys, values, (long long)"ep_sha256", 3LL); + ok = map_put(keys, values, (long long)"ep_net_connect", 1LL); + ok = map_put(keys, values, (long long)"ep_net_listen", 1LL); + ok = map_put(keys, values, (long long)"ep_net_accept", 1LL); + ok = map_put(keys, values, (long long)"ep_net_send", 1LL); + ok = map_put(keys, values, (long long)"ep_net_recv", 3LL); + ok = map_put(keys, values, (long long)"ep_net_close", 1LL); + ok = map_put(keys, values, (long long)"append_list", 1LL); + ok = map_put(keys, values, (long long)"get_list", 1LL); + ok = map_put(keys, values, (long long)"set_list", 1LL); + ok = map_put(keys, values, (long long)"length_list", 1LL); + ok = map_put(keys, values, (long long)"string_length", 1LL); + ok = map_put(keys, values, (long long)"get_character", 1LL); + ok = map_put(keys, values, (long long)"display_string", 1LL); + ok = map_put(keys, values, (long long)"get_argument_count", 1LL); + ok = map_put(keys, values, (long long)"get_argument", 2LL); + ok = map_put(keys, values, (long long)"write_file_content", 1LL); + ok = map_put(keys, values, (long long)"run_command", 1LL); + ok = map_put(keys, values, (long long)"substring", 3LL); + ok = map_put(keys, values, (long long)"string_from_list", 3LL); + ok = map_put(keys, values, (long long)"pop_list", 1LL); + ok = map_put(keys, values, (long long)"get_list_data_ptr", 1LL); + ok = map_put(keys, values, (long long)"sqlite_get_callback_ptr", 1LL); + ok = map_put(keys, values, (long long)"free_list", 1LL); + ok = map_put(keys, values, (long long)"create_map", 1LL); + ok = map_put(keys, values, (long long)"map_insert", 1LL); + ok = map_put(keys, values, (long long)"map_get_val", 1LL); + ok = map_put(keys, values, (long long)"map_contains", 1LL); + ok = map_put(keys, values, (long long)"map_delete", 1LL); + ok = map_put(keys, values, (long long)"free_map", 1LL); + ok = map_put(keys, values, (long long)"create_deque", 1LL); + ok = map_put(keys, values, (long long)"deque_push_back", 1LL); + ok = map_put(keys, values, (long long)"deque_push_front", 1LL); + ok = map_put(keys, values, (long long)"deque_pop_back", 1LL); + ok = map_put(keys, values, (long long)"deque_pop_front", 1LL); + ok = map_put(keys, values, (long long)"deque_length", 1LL); + ok = map_put(keys, values, (long long)"free_deque", 1LL); + ok = map_put(keys, values, (long long)"fs_scan_dir", 4LL); + ok = map_put(keys, values, (long long)"fs_copy_file", 1LL); + ok = map_put(keys, values, (long long)"fs_delete_file", 1LL); + ok = map_put(keys, values, (long long)"fs_move_file", 1LL); + ok = map_put(keys, values, (long long)"fs_exists", 1LL); + ok = map_put(keys, values, (long long)"fs_is_dir", 1LL); + ok = map_put(keys, values, (long long)"fs_is_file", 1LL); + ok = map_put(keys, values, (long long)"fs_get_size", 1LL); + ok = map_put(keys, values, (long long)"ep_http_request", 3LL); + ok = map_put(keys, values, (long long)"ep_sleep_ms", 1LL); + ok = map_put(keys, values, (long long)"concat", 3LL); + ok = map_put(keys, values, (long long)"ep_auto_to_string", 3LL); + ok = map_put(keys, values, (long long)"int_to_string", 3LL); + ok = map_put(keys, values, (long long)"ep_int_to_str", 3LL); + ok = map_put(keys, values, (long long)"string_upper", 3LL); + ok = map_put(keys, values, (long long)"string_lower", 3LL); + ok = map_put(keys, values, (long long)"string_trim", 3LL); + ok = map_put(keys, values, (long long)"string_replace", 3LL); + ok = map_put(keys, values, (long long)"string_split", 4LL); + ok = map_put(keys, values, (long long)"string_contains", 1LL); + ok = map_put(keys, values, (long long)"string_index_of", 1LL); + ok = map_put(keys, values, (long long)"string_to_int", 1LL); + ok = map_put(keys, values, (long long)"char_at", 3LL); + ok = map_put(keys, values, (long long)"char_from_code", 3LL); + ok = map_put(keys, values, (long long)"ptr_to_str", 3LL); + ok = map_put(keys, values, (long long)"str_to_ptr", 1LL); + ok = map_put(keys, values, (long long)"ep_sb_to_string", 3LL); + ok = map_put(keys, values, (long long)"ep_sb_create", 1LL); + ok = map_put(keys, values, (long long)"ep_sb_append", 1LL); + ok = map_put(keys, values, (long long)"ep_sb_append_int", 1LL); + ok = map_put(keys, values, (long long)"ep_random_int", 1LL); + ok = map_put(keys, values, (long long)"ep_abs", 1LL); + ok = map_put(keys, values, (long long)"ep_time_now_ms", 1LL); + ok = map_put(keys, values, (long long)"ep_time_now_sec", 1LL); + ok = map_put(keys, values, (long long)"ep_time_day", 1LL); + ok = map_put(keys, values, (long long)"ep_time_month", 1LL); + ok = map_put(keys, values, (long long)"ep_time_year", 1LL); + ok = map_put(keys, values, (long long)"sleep_ms", 1LL); + ok = map_put(keys, values, (long long)"ep_system", 1LL); + ok = map_put(keys, values, (long long)"int_to_float", 1LL); + ok = map_put(keys, values, (long long)"float_to_int", 1LL); + ok = map_put(keys, values, (long long)"ep_hmac_sha256", 3LL); + ok = map_put(keys, values, (long long)"ep_base64_encode", 3LL); + ok = map_put(keys, values, (long long)"ep_uuid_v4", 3LL); + ok = map_put(keys, values, (long long)"file_read", 3LL); + ok = map_put(keys, values, (long long)"file_write", 1LL); + ok = map_put(keys, values, (long long)"file_append", 1LL); + ok = map_put(keys, values, (long long)"file_exists", 1LL); + ok = map_put(keys, values, (long long)"read_line", 3LL); + ok = map_put(keys, values, (long long)"read_int", 1LL); + ok = map_put(keys, values, (long long)"ep_dlopen", 1LL); + ok = map_put(keys, values, (long long)"ep_dlsym", 1LL); + ok = map_put(keys, values, (long long)"ep_dlclose", 1LL); + ok = map_put(keys, values, (long long)"ep_dlcall0", 1LL); + ok = map_put(keys, values, (long long)"ep_dlcall1", 1LL); + ok = map_put(keys, values, (long long)"ep_dlcall2", 1LL); + ok = map_put(keys, values, (long long)"ep_dlcall3", 1LL); + ok = map_put(keys, values, (long long)"ep_dlcall4", 1LL); + ok = map_put(keys, values, (long long)"ep_dlcall5", 1LL); + ok = map_put(keys, values, (long long)"ep_dlcall6", 1LL); + ok = map_put(keys, values, (long long)"remove_list", 1LL); + ok = map_put(keys, values, (long long)"map_size", 1LL); + ok = map_put(keys, values, (long long)"map_get_str", 3LL); + ok = map_put(keys, values, (long long)"map_set_str", 1LL); + ok = map_put(keys, values, (long long)"map_keys", 4LL); + ok = map_put(keys, values, (long long)"map_values", 4LL); + ok = map_put(keys, values, (long long)"alloc_bytes", 1LL); + ok = map_put(keys, values, (long long)"free_bytes", 1LL); + ok = map_put(keys, values, (long long)"peek_byte", 1LL); + ok = map_put(keys, values, (long long)"poke_byte", 1LL); + ok = map_put(keys, values, (long long)"list_to_bytes", 1LL); + ok = map_put(keys, values, (long long)"bytes_to_list", 4LL); + ok = map_put(keys, values, (long long)"ep_gc_get_minor_count", 1LL); + ok = map_put(keys, values, (long long)"ep_gc_get_major_count", 1LL); + ok = map_put(keys, values, (long long)"ep_gc_get_nursery_count", 1LL); + ok = set_list(state, 10LL, length_list(keys)); + externals = get_list(program, 2LL); + ext_len = length_list(externals); + idx = 0LL; + while (idx < ext_len) { + ext_node = get_list(externals, idx); + ext_name = get_list(ext_node, 1LL); + existing = map_get(keys, values, ext_name); + if (existing == 0LL) { + ok = map_put(keys, values, ext_name, 1LL); + } + idx = (idx + 1LL); + } + funcs = get_list(program, 3LL); + funcs_len = length_list(funcs); + pass = 0LL; + while (pass < 3LL) { + idx = 0LL; + while (idx < funcs_len) { + func = get_list(funcs, idx); + name = get_list(func, 1LL); + params = get_list(func, 2LL); + body = get_list(func, 3LL); + var_keys = (create_list() + 0LL); + var_values = (create_list() + 0LL); + p_len = length_list(params); + p_idx = 0LL; + while (p_idx < p_len) { + p_node = get_list(params, p_idx); + p_name = get_list(p_node, 0LL); + is_borrow = get_list(p_node, 1LL); + param_type = 1LL; + if (is_borrow == 1LL) { + param_type = 5LL; + } + ok = map_put(var_keys, var_values, p_name, param_type); + p_idx = (p_idx + 1LL); + } + ok = collect_var_types(state, body, var_keys, var_values); + ret_t = determine_ret_type(state, body, var_keys, var_values); + if (ret_t == 0LL) { + ret_t = 1LL; + } + ok = map_put(keys, values, name, ret_t); + idx = (idx + 1LL); + } + pass = (pass + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long collect_var_types(long long state, long long stmts, long long var_keys, long long var_values) { + long long len = 0; + long long idx = 0; + long long stmt = 0; + long long type = 0; + long long var_name = 0; + long long expr = 0; + long long t = 0; + long long ok = 0; + long long then_b = 0; + long long else_b = 0; + long long body = 0; + long long arms = 0; + long long a_len = 0; + long long a_idx = 0; + long long arm = 0; + long long arm_body = 0; + long long fe_body = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = length_list(stmts); + idx = 0LL; + while (idx < len) { + stmt = get_list(stmts, idx); + type = get_list(stmt, 0LL); + if (type == 7LL) { + var_name = get_list(stmt, 1LL); + expr = get_list(stmt, 2LL); + t = infer_type(state, expr, var_keys, var_values); + ok = map_put(var_keys, var_values, var_name, t); + } else { + if (type == 10LL) { + then_b = get_list(stmt, 2LL); + ok = collect_var_types(state, then_b, var_keys, var_values); + else_b = get_list(stmt, 3LL); + if (else_b != 0LL) { + ok = collect_var_types(state, else_b, var_keys, var_values); + } + } else { + if (type == 11LL) { + body = get_list(stmt, 2LL); + ok = collect_var_types(state, body, var_keys, var_values); + } else { + if (type == 27LL) { + arms = get_list(stmt, 2LL); + a_len = length_list(arms); + a_idx = 0LL; + while (a_idx < a_len) { + arm = get_list(arms, a_idx); + arm_body = get_list(arm, 2LL); + ok = collect_var_types(state, arm_body, var_keys, var_values); + a_idx = (a_idx + 1LL); + } + } else { + if (type == 28LL) { + fe_body = get_list(stmt, 3LL); + ok = collect_var_types(state, fe_body, var_keys, var_values); + } + } + } + } + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long determine_ret_type(long long state, long long stmts, long long var_keys, long long var_values) { + long long len = 0; + long long idx = 0; + long long stmt = 0; + long long type = 0; + long long expr = 0; + long long then_b = 0; + long long ret_t = 0; + long long else_b = 0; + long long ret_t2 = 0; + long long body = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = length_list(stmts); + idx = 0LL; + while (idx < len) { + stmt = get_list(stmts, idx); + type = get_list(stmt, 0LL); + if (type == 8LL) { + expr = get_list(stmt, 1LL); + ret_val = infer_type(state, expr, var_keys, var_values); + goto L_cleanup; + } else { + if (type == 10LL) { + then_b = get_list(stmt, 2LL); + ret_t = determine_ret_type(state, then_b, var_keys, var_values); + if (ret_t != 0LL) { + ret_val = ret_t; + goto L_cleanup; + } + else_b = get_list(stmt, 3LL); + if (else_b != 0LL) { + ret_t2 = determine_ret_type(state, else_b, var_keys, var_values); + if (ret_t2 != 0LL) { + ret_val = ret_t2; + goto L_cleanup; + } + } + } else { + if (type == 11LL) { + body = get_list(stmt, 2LL); + ret_t = determine_ret_type(state, body, var_keys, var_values); + if (ret_t != 0LL) { + ret_val = ret_t; + goto L_cleanup; + } + } + } + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long infer_type(long long state, long long expr, long long var_keys, long long var_values) { + long long type = 0; + long long name = 0; + long long t = 0; + long long func_keys = 0; + long long func_values = 0; + long long inner = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + type = get_list(expr, 0LL); + if (type == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (type == 2LL) { + ret_val = 2LL; + goto L_cleanup; + } + if (type == 3LL) { + name = get_list(expr, 1LL); + t = map_get(var_keys, var_values, name); + if (t != 0LL) { + ret_val = t; + goto L_cleanup; + } + ret_val = 1LL; + goto L_cleanup; + } + if (((type == 4LL || type == 5LL) || type == 14LL)) { + ret_val = 1LL; + goto L_cleanup; + } + if ((type == 31LL || type == 32LL)) { + ret_val = 7LL; + goto L_cleanup; + } + if (type == 6LL) { + name = get_list(expr, 1LL); + func_keys = get_list(state, 3LL); + func_values = get_list(state, 4LL); + t = map_get(func_keys, func_values, name); + if (t != 0LL) { + ret_val = t; + goto L_cleanup; + } + ret_val = 1LL; + goto L_cleanup; + } + if (type == 20LL) { + inner = get_list(expr, 1LL); + t = infer_type(state, inner, var_keys, var_values); + if ((t == 4LL || t == 5LL)) { + ret_val = 5LL; + goto L_cleanup; + } + if (((t == 2LL || t == 3LL) || t == 6LL)) { + ret_val = 6LL; + goto L_cleanup; + } + ret_val = 5LL; + goto L_cleanup; + } + if (type == 21LL) { + inner = get_list(expr, 1LL); + ret_val = infer_type(state, inner, var_keys, var_values); + goto L_cleanup; + } + ret_val = 1LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long is_global_var(long long name) { + long long len = 0; + long long has_upper = 0; + long long idx = 0; + long long ch = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = string_length((char*)name); + if (len == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + has_upper = 0LL; + idx = 0LL; + while (idx < len) { + ch = get_character((char*)name, idx); + if ((ch >= 97LL && ch <= 122LL)) { + ret_val = 0LL; + goto L_cleanup; + } + if ((ch >= 65LL && ch <= 90LL)) { + has_upper = 1LL; + } + idx = (idx + 1LL); + } + ret_val = has_upper; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long cg_string_contains(long long s, long long sub) { + long long len = 0; + long long sub_len = 0; + long long limit = 0; + long long i = 0; + long long match = 0; + long long j = 0; + long long c1 = 0; + long long c2 = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = string_length((char*)s); + sub_len = string_length((char*)sub); + if (sub_len > len) { + ret_val = 0LL; + goto L_cleanup; + } + if (sub_len == 0LL) { + ret_val = 1LL; + goto L_cleanup; + } + limit = ((len - sub_len) + 1LL); + i = 0LL; + while (i < limit) { + match = 1LL; + j = 0LL; + while ((j < sub_len && match == 1LL)) { + c1 = get_character((char*)s, (i + j)); + c2 = get_character((char*)sub, j); + if (c1 != c2) { + match = 0LL; + } + j = (j + 1LL); + } + if (match == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + i = (i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long str_starts_with(long long s, long long prefix) { + long long slen = 0; + long long plen = 0; + long long i = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + slen = string_length((char*)s); + plen = string_length((char*)prefix); + if (plen > slen) { + ret_val = 0LL; + goto L_cleanup; + } + i = 0LL; + while (i < plen) { + if (get_character((char*)s, i) != get_character((char*)prefix, i)) { + ret_val = 0LL; + goto L_cleanup; + } + i = (i + 1LL); + } + ret_val = 1LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long str_ends_with(long long s, long long suffix) { + long long slen = 0; + long long xlen = 0; + long long off = 0; + long long i = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + slen = string_length((char*)s); + xlen = string_length((char*)suffix); + if (xlen > slen) { + ret_val = 0LL; + goto L_cleanup; + } + off = (slen - xlen); + i = 0LL; + while (i < xlen) { + if (get_character((char*)s, (off + i)) != get_character((char*)suffix, i)) { + ret_val = 0LL; + goto L_cleanup; + } + i = (i + 1LL); + } + ret_val = 1LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long is_accessor_name(long long name) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + if ((strcmp((char*)(long long)"get", (char*)name) == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + if ((strcmp((char*)(long long)"peek", (char*)name) == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + if (str_starts_with(name, (long long)"get_") == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (str_starts_with(name, (long long)"peek_") == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (str_ends_with(name, (long long)"_get") == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (str_ends_with(name, (long long)"_peek") == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long is_borrow_expr(long long expr, long long borrowed_keys, long long borrowed_values) { + long long type = 0; + long long func_name = 0; + long long name = 0; + long long is_borrowed = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + type = get_list(expr, 0LL); + if (type == 20LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (type == 6LL) { + func_name = get_list(expr, 1LL); + if (is_accessor_name(func_name) == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + } + if (type == 3LL) { + name = get_list(expr, 1LL); + is_borrowed = map_get(borrowed_keys, borrowed_values, name); + if (is_borrowed == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long scan_stmts_for_borrows(long long stmts, long long borrowed_keys, long long borrowed_values) { + long long len = 0; + long long idx = 0; + long long stmt = 0; + long long type = 0; + long long var_name = 0; + long long expr = 0; + long long is_borrow = 0; + long long ok = 0; + long long then_b = 0; + long long else_b = 0; + long long body = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = length_list(stmts); + idx = 0LL; + while (idx < len) { + stmt = get_list(stmts, idx); + type = get_list(stmt, 0LL); + if (type == 7LL) { + var_name = get_list(stmt, 1LL); + expr = get_list(stmt, 2LL); + is_borrow = is_borrow_expr(expr, borrowed_keys, borrowed_values); + if (is_borrow == 1LL) { + ok = map_put(borrowed_keys, borrowed_values, var_name, 1LL); + } else { + ok = map_put(borrowed_keys, borrowed_values, var_name, 0LL); + } + } + if (type == 10LL) { + then_b = get_list(stmt, 2LL); + ok = scan_stmts_for_borrows(then_b, borrowed_keys, borrowed_values); + else_b = get_list(stmt, 3LL); + if (else_b != 0LL) { + ok = scan_stmts_for_borrows(else_b, borrowed_keys, borrowed_values); + } + } + if (type == 11LL) { + body = get_list(stmt, 2LL); + ok = scan_stmts_for_borrows(body, borrowed_keys, borrowed_values); + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long collect_borrowed_vars(long long stmts, long long params, long long borrowed_keys, long long borrowed_values) { + long long p_len = 0; + long long idx = 0; + long long p_node = 0; + long long p_name = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + p_len = length_list(params); + idx = 0LL; + while (idx < p_len) { + p_node = get_list(params, idx); + p_name = get_list(p_node, 0LL); + ok = map_put(borrowed_keys, borrowed_values, p_name, 1LL); + idx = (idx + 1LL); + } + ok = scan_stmts_for_borrows(stmts, borrowed_keys, borrowed_values); + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long gen_function(long long state, long long func) { + long long name = 0; + long long params = 0; + long long body = 0; + long long is_async = 0; + long long func_keys = 0; + long long func_values = 0; + long long ret_t = 0; + long long ok = 0; + long long var_types_keys = 0; + long long var_types_values = 0; + long long p_len = 0; + long long p_idx = 0; + long long p_node = 0; + long long p_name = 0; + long long is_borrow = 0; + long long param_type = 0; + long long struct_decl = 0; + long long j = 0; + long long wrap_fn = 0; + long long pub_fn = 0; + long long impl_name = 0; + long long header = 0; + long long borrowed_keys = 0; + long long borrowed_values = 0; + long long num_vars = 0; + long long idx = 0; + long long var_name = 0; + long long is_param = 0; + long long p_i = 0; + long long is_global = 0; + long long decl = 0; + long long gc_root_count = 0; + long long is_p = 0; + long long t = 0; + long long root_line = 0; + long long body_len = 0; + long long stmt = 0; + long long gc_count_str = 0; + long long root_pop = 0; + long long is_borrowed = 0; + long long cleanup_line = 0; + long long ret_val = 0; + + ep_gc_push_root(&struct_decl); + ep_gc_push_root(&wrap_fn); + ep_gc_push_root(&pub_fn); + ep_gc_push_root(&impl_name); + ep_gc_push_root(&header); + ep_gc_push_root(&decl); + ep_gc_push_root(&root_line); + ep_gc_push_root(&root_pop); + ep_gc_push_root(&cleanup_line); + ep_gc_maybe_collect(); + + name = get_list(func, 1LL); + params = get_list(func, 2LL); + body = get_list(func, 3LL); + is_async = 0LL; + if (length_list(func) > 4LL) { + is_async = get_list(func, 4LL); + } + func_keys = get_list(state, 3LL); + func_values = get_list(state, 4LL); + ret_t = map_get(func_keys, func_values, name); + ok = set_list(state, 5LL, ret_t); + var_types_keys = (create_list() + 0LL); + var_types_values = (create_list() + 0LL); + p_len = length_list(params); + p_idx = 0LL; + while (p_idx < p_len) { + p_node = get_list(params, p_idx); + p_name = get_list(p_node, 0LL); + is_borrow = get_list(p_node, 1LL); + param_type = 1LL; + if (is_borrow == 1LL) { + param_type = 5LL; + } + ok = map_put(var_types_keys, var_types_values, p_name, param_type); + p_idx = (p_idx + 1LL); + } + ok = collect_var_types(state, body, var_types_keys, var_types_values); + if (is_async == 1LL) { + struct_decl = (long long)"typedef struct {\n EpFuture* fut;\n"; + j = 0LL; + while (j < p_len) { + struct_decl = string_concat(struct_decl, (long long)" long long arg"); + struct_decl = string_concat(struct_decl, cg_int_to_str(j)); + struct_decl = string_concat(struct_decl, (long long)";\n"); + j = (j + 1LL); + } + if (p_len == 0LL) { + struct_decl = string_concat(struct_decl, (long long)" int dummy;\n"); + } + struct_decl = string_concat(struct_decl, (long long)"} "); + struct_decl = string_concat(struct_decl, get_fn_c_name(func)); + struct_decl = string_concat(struct_decl, (long long)"_async_args;\n\n"); + ok = emit(state, struct_decl); + wrap_fn = (long long)"void* "; + wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); + wrap_fn = string_concat(wrap_fn, (long long)"_async_wrapper(void* r) {\n"); + wrap_fn = string_concat(wrap_fn, (long long)" int stack_dummy;\n"); + wrap_fn = string_concat(wrap_fn, (long long)" ep_gc_register_thread(&stack_dummy);\n"); + wrap_fn = string_concat(wrap_fn, (long long)" "); + wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); + wrap_fn = string_concat(wrap_fn, (long long)"_async_args* args = ("); + wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); + wrap_fn = string_concat(wrap_fn, (long long)"_async_args*)r;\n"); + wrap_fn = string_concat(wrap_fn, (long long)" long long res = "); + wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); + wrap_fn = string_concat(wrap_fn, (long long)"_impl("); + j = 0LL; + while (j < p_len) { + wrap_fn = string_concat(wrap_fn, (long long)"args->arg"); + wrap_fn = string_concat(wrap_fn, cg_int_to_str(j)); + if (j < (p_len - 1LL)) { + wrap_fn = string_concat(wrap_fn, (long long)", "); + } + j = (j + 1LL); + } + wrap_fn = string_concat(wrap_fn, (long long)");\n"); + wrap_fn = string_concat(wrap_fn, (long long)" args->fut->value = res;\n"); + wrap_fn = string_concat(wrap_fn, (long long)" args->fut->completed = 1;\n"); + wrap_fn = string_concat(wrap_fn, (long long)" send_channel(args->fut->chan, res);\n"); + wrap_fn = string_concat(wrap_fn, (long long)" free(args);\n"); + wrap_fn = string_concat(wrap_fn, (long long)" ep_gc_unregister_thread();\n"); + wrap_fn = string_concat(wrap_fn, (long long)" return NULL;\n"); + wrap_fn = string_concat(wrap_fn, (long long)"}\n\n"); + ok = emit(state, wrap_fn); + pub_fn = (long long)"long long "; + pub_fn = string_concat(pub_fn, get_fn_c_name(func)); + pub_fn = string_concat(pub_fn, (long long)"("); + j = 0LL; + while (j < p_len) { + p_node = get_list(params, j); + p_name = get_list(p_node, 0LL); + pub_fn = string_concat(pub_fn, (long long)"long long "); + pub_fn = string_concat(pub_fn, p_name); + if (j < (p_len - 1LL)) { + pub_fn = string_concat(pub_fn, (long long)", "); + } + j = (j + 1LL); + } + pub_fn = string_concat(pub_fn, (long long)") {\n"); + pub_fn = string_concat(pub_fn, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n"); + pub_fn = string_concat(pub_fn, (long long)" fut->chan = create_channel();\n"); + pub_fn = string_concat(pub_fn, (long long)" fut->completed = 0;\n"); + pub_fn = string_concat(pub_fn, (long long)" fut->value = 0;\n"); + pub_fn = string_concat(pub_fn, (long long)" ep_gc_register(fut, EP_OBJ_STRUCT);\n"); + pub_fn = string_concat(pub_fn, (long long)" "); + pub_fn = string_concat(pub_fn, get_fn_c_name(func)); + pub_fn = string_concat(pub_fn, (long long)"_async_args* args = ("); + pub_fn = string_concat(pub_fn, get_fn_c_name(func)); + pub_fn = string_concat(pub_fn, (long long)"_async_args*)malloc(sizeof("); + pub_fn = string_concat(pub_fn, get_fn_c_name(func)); + pub_fn = string_concat(pub_fn, (long long)"_async_args));\n"); + pub_fn = string_concat(pub_fn, (long long)" args->fut = fut;\n"); + j = 0LL; + while (j < p_len) { + p_node = get_list(params, j); + p_name = get_list(p_node, 0LL); + pub_fn = string_concat(pub_fn, (long long)" args->arg"); + pub_fn = string_concat(pub_fn, cg_int_to_str(j)); + pub_fn = string_concat(pub_fn, (long long)" = "); + pub_fn = string_concat(pub_fn, p_name); + pub_fn = string_concat(pub_fn, (long long)";\n"); + j = (j + 1LL); + } + pub_fn = string_concat(pub_fn, (long long)" pthread_t thread;\n"); + pub_fn = string_concat(pub_fn, (long long)" pthread_create(&thread, NULL, "); + pub_fn = string_concat(pub_fn, get_fn_c_name(func)); + pub_fn = string_concat(pub_fn, (long long)"_async_wrapper, args);\n"); + pub_fn = string_concat(pub_fn, (long long)" pthread_detach(thread);\n"); + pub_fn = string_concat(pub_fn, (long long)" return (long long)fut;\n"); + pub_fn = string_concat(pub_fn, (long long)"}\n\n"); + ok = emit(state, pub_fn); + } + impl_name = get_fn_c_name(func); + if (is_async == 1LL) { + impl_name = string_concat(get_fn_c_name(func), (long long)"_impl"); + } + header = (long long)"long long "; + header = string_concat(header, impl_name); + header = string_concat(header, (long long)"("); + p_idx = 0LL; + while (p_idx < p_len) { + p_node = get_list(params, p_idx); + p_name = get_list(p_node, 0LL); + header = string_concat(header, (long long)"long long "); + header = string_concat(header, p_name); + if (p_idx < (p_len - 1LL)) { + header = string_concat(header, (long long)", "); + } + p_idx = (p_idx + 1LL); + } + header = string_concat(header, (long long)") {\n"); + ok = emit(state, header); + borrowed_keys = (create_list() + 0LL); + borrowed_values = (create_list() + 0LL); + ok = collect_borrowed_vars(body, params, borrowed_keys, borrowed_values); + ok = set_codegen_borrowed_keys(state, borrowed_keys); + ok = set_codegen_borrowed_values(state, borrowed_values); + num_vars = length_list(var_types_keys); + idx = 0LL; + while (idx < num_vars) { + var_name = get_list(var_types_keys, idx); + is_param = 0LL; + p_i = 0LL; + while (p_i < p_len) { + p_node = get_list(params, p_i); + p_name = get_list(p_node, 0LL); + if (var_name == p_name) { + is_param = 1LL; + } + p_i = (p_i + 1LL); + } + if (is_param == 0LL) { + is_global = is_global_var(var_name); + if (is_global == 0LL) { + decl = (long long)" long long "; + decl = string_concat(decl, var_name); + decl = string_concat(decl, (long long)" = 0;\n"); + ok = emit(state, decl); + } + } + idx = (idx + 1LL); + } + ok = emit(state, (long long)" long long ret_val = 0;\n\n"); + gc_root_count = 0LL; + idx = 0LL; + while (idx < num_vars) { + var_name = get_list(var_types_keys, idx); + is_p = 0LL; + p_i = 0LL; + while (p_i < p_len) { + p_node = get_list(params, p_i); + p_name = get_list(p_node, 0LL); + if (var_name == p_name) { + is_p = 1LL; + } + p_i = (p_i + 1LL); + } + if (is_p == 0LL) { + is_global = is_global_var(var_name); + if (is_global == 0LL) { + t = map_get(var_types_keys, var_types_values, var_name); + if ((t == 3LL || t == 4LL)) { + root_line = (long long)" ep_gc_push_root(&"; + root_line = string_concat(root_line, var_name); + root_line = string_concat(root_line, (long long)");\n"); + ok = emit(state, root_line); + gc_root_count = (gc_root_count + 1LL); + } + } + } + idx = (idx + 1LL); + } + ok = emit(state, (long long)" ep_gc_maybe_collect();\n\n"); + body_len = length_list(body); + idx = 0LL; + while (idx < body_len) { + stmt = get_list(body, idx); + ok = gen_statement(state, stmt, var_types_keys, var_types_values); + idx = (idx + 1LL); + } + ok = emit(state, (long long)"L_cleanup:\n"); + if (gc_root_count > 0LL) { + gc_count_str = cg_int_to_str(gc_root_count); + root_pop = (long long)" ep_gc_pop_roots("; + root_pop = string_concat(root_pop, gc_count_str); + root_pop = string_concat(root_pop, (long long)");\n"); + ok = emit(state, root_pop); + } + idx = 0LL; + while (idx < num_vars) { + var_name = get_list(var_types_keys, idx); + is_param = 0LL; + p_i = 0LL; + while (p_i < p_len) { + p_node = get_list(params, p_i); + p_name = get_list(p_node, 0LL); + if (var_name == p_name) { + is_param = 1LL; + } + p_i = (p_i + 1LL); + } + if (is_param == 0LL) { + is_global = is_global_var(var_name); + is_borrowed = map_get(borrowed_keys, borrowed_values, var_name); + if ((is_global == 0LL && is_borrowed == 0LL)) { + t = map_get(var_types_keys, var_types_values, var_name); + if (t == 4LL) { + cleanup_line = (long long)" free_list("; + cleanup_line = string_concat(cleanup_line, var_name); + cleanup_line = string_concat(cleanup_line, (long long)");\n"); + ok = emit(state, cleanup_line); + } + } + } + idx = (idx + 1LL); + } + ok = emit(state, (long long)" return ret_val;\n}\n\n"); + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(9); + return ret_val; +} + +long long gen_statement(long long state, long long stmt, long long var_keys, long long var_values) { + long long type = 0; + long long name = 0; + long long expr = 0; + long long t = 0; + long long expr_str = 0; + long long is_global = 0; + long long borrowed_keys = 0; + long long borrowed_values = 0; + long long is_borrowed = 0; + long long ok = 0; + long long line = 0; + long long expr_type = 0; + long long null_line = 0; + long long cond = 0; + long long then_b = 0; + long long else_b = 0; + long long cond_str = 0; + long long t_len = 0; + long long t_idx = 0; + long long s = 0; + long long e_len = 0; + long long e_idx = 0; + long long body = 0; + long long b_len = 0; + long long b_idx = 0; + long long func_name = 0; + long long args = 0; + long long args_len = 0; + long long idx_val = 0; + long long j = 0; + long long arg = 0; + long long arg_str = 0; + long long chan = 0; + long long val = 0; + long long chan_str = 0; + long long val_str = 0; + long long val_type = 0; + long long obj = 0; + long long field_name = 0; + long long obj_str = 0; + long long arms = 0; + long long arm_len = 0; + long long arm_idx = 0; + long long arm = 0; + long long vname = 0; + long long bindings = 0; + long long arm_body = 0; + long long kw = 0; + long long bname = 0; + long long ab_len = 0; + long long ab_idx = 0; + long long var_name = 0; + long long iter_expr = 0; + long long iter_str = 0; + long long label = 0; + long long ret_val = 0; + + ep_gc_push_root(&expr_str); + ep_gc_push_root(&line); + ep_gc_push_root(&null_line); + ep_gc_push_root(&cond_str); + ep_gc_push_root(&arg_str); + ep_gc_push_root(&chan_str); + ep_gc_push_root(&val_str); + ep_gc_push_root(&obj_str); + ep_gc_push_root(&iter_str); + ep_gc_push_root(&label); + ep_gc_maybe_collect(); + + type = get_list(stmt, 0LL); + if (type == 7LL) { + name = get_list(stmt, 1LL); + expr = get_list(stmt, 2LL); + t = map_get(var_keys, var_values, name); + expr_str = gen_expr(state, expr, var_keys, var_values); + is_global = is_global_var(name); + borrowed_keys = get_codegen_borrowed_keys(state); + borrowed_values = get_codegen_borrowed_values(state); + is_borrowed = map_get(borrowed_keys, borrowed_values, name); + if (((t == 4LL && is_global == 0LL) && is_borrowed == 0LL)) { + ok = emit(state, (long long)" {\n"); + line = (long long)" long long tmp_val = "; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + line = (long long)" free_list("; + line = string_concat(line, name); + line = string_concat(line, (long long)");\n"); + ok = emit(state, line); + line = (long long)" "; + line = string_concat(line, name); + line = string_concat(line, (long long)" = tmp_val;\n"); + ok = emit(state, line); + ok = emit(state, (long long)" }\n"); + } else { + line = (long long)" "; + line = string_concat(line, name); + line = string_concat(line, (long long)" = "); + line = string_concat(line, expr_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + } + ret_val = 0LL; + goto L_cleanup; + } + if (type == 8LL) { + expr = get_list(stmt, 1LL); + expr_str = gen_expr(state, expr, var_keys, var_values); + line = (long long)" ret_val = "; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + expr_type = get_list(expr, 0LL); + if (expr_type == 3LL) { + name = get_list(expr, 1LL); + t = map_get(var_keys, var_values, name); + if (t == 4LL) { + null_line = (long long)" "; + null_line = string_concat(null_line, name); + null_line = string_concat(null_line, (long long)" = 0;\n"); + ok = emit(state, null_line); + } + } + ok = emit(state, (long long)" goto L_cleanup;\n"); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 9LL) { + expr = get_list(stmt, 1LL); + t = infer_type(state, expr, var_keys, var_values); + expr_str = gen_expr(state, expr, var_keys, var_values); + if ((t == 2LL || t == 3LL)) { + line = (long long)" printf(\"%s\\n\", (char*)"; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)");\n"); + ok = emit(state, line); + } else { + if (t == 7LL) { + line = (long long)" printf(\"%s\\n\", ("; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)") ? \"true\" : \"false\");\n"); + ok = emit(state, line); + } else { + line = (long long)" printf(\"%lld\\n\", "; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)");\n"); + ok = emit(state, line); + } + } + ret_val = 0LL; + goto L_cleanup; + } + if (type == 10LL) { + cond = get_list(stmt, 1LL); + then_b = get_list(stmt, 2LL); + else_b = get_list(stmt, 3LL); + cond_str = gen_expr(state, cond, var_keys, var_values); + line = (long long)" if ("; + line = string_concat(line, cond_str); + line = string_concat(line, (long long)") {\n"); + ok = emit(state, line); + t_len = length_list(then_b); + t_idx = 0LL; + while (t_idx < t_len) { + s = get_list(then_b, t_idx); + ok = gen_statement(state, s, var_keys, var_values); + t_idx = (t_idx + 1LL); + } + if (else_b != 0LL) { + ok = emit(state, (long long)" } else {\n"); + e_len = length_list(else_b); + e_idx = 0LL; + while (e_idx < e_len) { + s = get_list(else_b, e_idx); + ok = gen_statement(state, s, var_keys, var_values); + e_idx = (e_idx + 1LL); + } + ok = emit(state, (long long)" }\n"); + } else { + ok = emit(state, (long long)" }\n"); + } + ret_val = 0LL; + goto L_cleanup; + } + if (type == 11LL) { + cond = get_list(stmt, 1LL); + body = get_list(stmt, 2LL); + cond_str = gen_expr(state, cond, var_keys, var_values); + line = (long long)" while ("; + line = string_concat(line, cond_str); + line = string_concat(line, (long long)") {\n"); + ok = emit(state, line); + b_len = length_list(body); + b_idx = 0LL; + while (b_idx < b_len) { + s = get_list(body, b_idx); + ok = gen_statement(state, s, var_keys, var_values); + b_idx = (b_idx + 1LL); + } + ok = emit(state, (long long)" }\n"); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 15LL) { + func_name = get_list(stmt, 1LL); + args = get_list(stmt, 2LL); + args_len = length_list(args); + idx_val = get_codegen_spawn_index(state); + ok = set_codegen_spawn_index(state, (idx_val + 1LL)); + ok = emit(state, (long long)" {\n"); + line = (long long)" spawn_args_"; + line = string_concat(line, cg_int_to_str(idx_val)); + line = string_concat(line, (long long)"* s_args = malloc(sizeof(spawn_args_"); + line = string_concat(line, cg_int_to_str(idx_val)); + line = string_concat(line, (long long)"));\n"); + ok = emit(state, line); + j = 0LL; + while (j < args_len) { + arg = get_list(args, j); + arg_str = gen_expr(state, arg, var_keys, var_values); + line = (long long)" s_args->arg"; + line = string_concat(line, cg_int_to_str(j)); + line = string_concat(line, (long long)" = "); + line = string_concat(line, arg_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + j = (j + 1LL); + } + ok = emit(state, (long long)" pthread_t t;\n"); + line = (long long)" pthread_create(&t, NULL, spawn_wrapper_"; + line = string_concat(line, cg_int_to_str(idx_val)); + line = string_concat(line, (long long)", s_args);\n"); + ok = emit(state, line); + ok = emit(state, (long long)" pthread_detach(t);\n"); + ok = emit(state, (long long)" }\n"); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 16LL) { + chan = get_list(stmt, 1LL); + val = get_list(stmt, 2LL); + chan_str = gen_expr(state, chan, var_keys, var_values); + val_str = gen_expr(state, val, var_keys, var_values); + line = (long long)" send_channel("; + line = string_concat(line, chan_str); + line = string_concat(line, (long long)", "); + line = string_concat(line, val_str); + line = string_concat(line, (long long)");\n"); + ok = emit(state, line); + val_type = get_list(val, 0LL); + if (val_type == 3LL) { + name = get_list(val, 1LL); + t = map_get(var_keys, var_values, name); + if (t == 4LL) { + null_line = (long long)" "; + null_line = string_concat(null_line, name); + null_line = string_concat(null_line, (long long)" = 0;\n"); + ok = emit(state, null_line); + } + } + ret_val = 0LL; + goto L_cleanup; + } + if (type == 23LL) { + obj = get_list(stmt, 1LL); + field_name = get_list(stmt, 2LL); + val = get_list(stmt, 3LL); + obj_str = gen_expr(state, obj, var_keys, var_values); + val_str = gen_expr(state, val, var_keys, var_values); + line = (long long)" ((long long*)"; + line = string_concat(line, obj_str); + line = string_concat(line, (long long)")[EP_FIELD_"); + line = string_concat(line, field_name); + line = string_concat(line, (long long)"] = "); + line = string_concat(line, val_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 27LL) { + expr = get_list(stmt, 1LL); + arms = get_list(stmt, 2LL); + expr_str = gen_expr(state, expr, var_keys, var_values); + ok = emit(state, (long long)" {\n"); + line = (long long)" long long _match_val = "; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + ok = emit(state, (long long)" long long _match_tag = ((long long*)_match_val)[0];\n"); + arm_len = length_list(arms); + arm_idx = 0LL; + while (arm_idx < arm_len) { + arm = get_list(arms, arm_idx); + vname = get_list(arm, 0LL); + bindings = get_list(arm, 1LL); + arm_body = get_list(arm, 2LL); + kw = (long long)"if"; + if (arm_idx > 0LL) { + kw = (long long)"} else if"; + } + line = (long long)" "; + line = string_concat(line, kw); + line = string_concat(line, (long long)" (_match_tag == EP_TAG_"); + line = string_concat(line, vname); + line = string_concat(line, (long long)") {\n"); + ok = emit(state, line); + b_len = length_list(bindings); + b_idx = 0LL; + while (b_idx < b_len) { + bname = get_list(bindings, b_idx); + line = (long long)" long long "; + line = string_concat(line, bname); + line = string_concat(line, (long long)" = ((long long*)_match_val)["); + line = string_concat(line, cg_int_to_str((b_idx + 1LL))); + line = string_concat(line, (long long)"];\n"); + ok = emit(state, line); + b_idx = (b_idx + 1LL); + } + ab_len = length_list(arm_body); + ab_idx = 0LL; + while (ab_idx < ab_len) { + s = get_list(arm_body, ab_idx); + ok = gen_statement(state, s, var_keys, var_values); + ab_idx = (ab_idx + 1LL); + } + arm_idx = (arm_idx + 1LL); + } + ok = emit(state, (long long)" }\n"); + ok = emit(state, (long long)" }\n"); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 28LL) { + var_name = get_list(stmt, 1LL); + iter_expr = get_list(stmt, 2LL); + body = get_list(stmt, 3LL); + iter_str = gen_expr(state, iter_expr, var_keys, var_values); + label = get_new_label(state, (long long)"foreach"); + ok = emit(state, (long long)" {\n"); + line = (long long)" long long _iter = "; + line = string_concat(line, iter_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + ok = emit(state, (long long)" long long _iter_len = length_list(_iter);\n"); + ok = emit(state, (long long)" long long _iter_i = 0;\n"); + ok = emit(state, (long long)" while (_iter_i < _iter_len) {\n"); + line = (long long)" long long "; + line = string_concat(line, var_name); + line = string_concat(line, (long long)" = get_list(_iter, _iter_i);\n"); + ok = emit(state, line); + b_len = length_list(body); + b_idx = 0LL; + while (b_idx < b_len) { + s = get_list(body, b_idx); + ok = gen_statement(state, s, var_keys, var_values); + b_idx = (b_idx + 1LL); + } + ok = emit(state, (long long)" _iter_i++;\n"); + ok = emit(state, (long long)" }\n"); + ok = emit(state, (long long)" }\n"); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 29LL) { + ok = emit(state, (long long)" break;\n"); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 30LL) { + ok = emit(state, (long long)" continue;\n"); + ret_val = 0LL; + goto L_cleanup; + } + if (type == 36LL) { + expr = get_list(stmt, 1LL); + expr_str = gen_expr(state, expr, var_keys, var_values); + line = (long long)" "; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)";\n"); + ok = emit(state, line); + ret_val = 0LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(10); + return ret_val; +} + +long long gen_expr(long long state, long long expr, long long var_keys, long long var_values) { + long long type = 0; + long long val = 0; + long long num_str = 0; + long long escaped = 0; + long long res = 0; + long long name = 0; + long long eres = 0; + long long fk = 0; + long long fres = 0; + long long left = 0; + long long op = 0; + long long right = 0; + long long left_str = 0; + long long right_str = 0; + long long op_str = 0; + long long lt = 0; + long long is_string = 0; + long long cmp_op = 0; + long long args = 0; + long long args_len = 0; + long long formatted_args = 0; + long long idx = 0; + long long arg = 0; + long long arg_val = 0; + long long needs_cast = 0; + long long casted = 0; + long long ok = 0; + long long args_str_list = 0; + long long arg_item = 0; + long long args_joined = 0; + long long func_keys = 0; + long long cl_sig = 0; + long long rw_sig = 0; + long long sg_i = 0; + long long call_str = 0; + long long chan = 0; + long long chan_str = 0; + long long inner = 0; + long long inner_str = 0; + long long obj = 0; + long long field_name = 0; + long long obj_str = 0; + long long struct_name = 0; + long long fields = 0; + long long field_count = 0; + long long f_idx = 0; + long long fpair = 0; + long long fname = 0; + long long fval = 0; + long long fval_str = 0; + long long method_name = 0; + long long arg_str = 0; + long long variant_name = 0; + long long alloc_size = 0; + long long a_idx = 0; + long long params = 0; + long long body = 0; + long long cidx = 0; + long long dummy = 0; + long long cname = 0; + long long raw_names = 0; + long long okr = 0; + long long p_len = 0; + long long captured = 0; + long long rn_len = 0; + long long rn_i = 0; + long long nm = 0; + long long skip = 0; + long long pp_i = 0; + long long p_node = 0; + long long okc = 0; + long long n_caps = 0; + long long saved_lines = 0; + long long fresh = 0; + long long dummy2 = 0; + long long hdr = 0; + long long hp_i = 0; + long long okh = 0; + long long cp_i = 0; + long long unp = 0; + long long oku = 0; + long long c_keys = 0; + long long c_values = 0; + long long ov_len = 0; + long long ov_i = 0; + long long okov = 0; + long long pv_i = 0; + long long okpv = 0; + long long b_keys = 0; + long long b_values = 0; + long long okbv = 0; + long long bv_len = 0; + long long bv_i = 0; + long long bname = 0; + long long is_p = 0; + long long bp_i = 0; + long long dec = 0; + long long okd = 0; + long long okbm = 0; + long long bs_len = 0; + long long bs_i = 0; + long long okst = 0; + long long okft = 0; + long long closure_text = 0; + long long dummy3 = 0; + long long cbodies = 0; + long long okcb = 0; + long long ce_i = 0; + long long elements = 0; + long long elem_len = 0; + long long e_idx = 0; + long long elem = 0; + long long elem_str = 0; + long long ret_val = 0; + + ep_gc_push_root(&escaped); + ep_gc_push_root(&res); + ep_gc_push_root(&eres); + ep_gc_push_root(&fres); + ep_gc_push_root(&left_str); + ep_gc_push_root(&right_str); + ep_gc_push_root(&formatted_args); + ep_gc_push_root(&arg_val); + ep_gc_push_root(&casted); + ep_gc_push_root(&args_str_list); + ep_gc_push_root(&args_joined); + ep_gc_push_root(&cl_sig); + ep_gc_push_root(&rw_sig); + ep_gc_push_root(&call_str); + ep_gc_push_root(&chan_str); + ep_gc_push_root(&inner_str); + ep_gc_push_root(&obj_str); + ep_gc_push_root(&fval_str); + ep_gc_push_root(&arg_str); + ep_gc_push_root(&cname); + ep_gc_push_root(&raw_names); + ep_gc_push_root(&captured); + ep_gc_push_root(&nm); + ep_gc_push_root(&hdr); + ep_gc_push_root(&unp); + ep_gc_push_root(&c_keys); + ep_gc_push_root(&c_values); + ep_gc_push_root(&b_keys); + ep_gc_push_root(&b_values); + ep_gc_push_root(&bname); + ep_gc_push_root(&dec); + ep_gc_push_root(&closure_text); + ep_gc_push_root(&elem_str); + ep_gc_maybe_collect(); + + type = get_list(expr, 0LL); + if (type == 1LL) { + val = get_list(expr, 1LL); + num_str = cg_int_to_str(val); + ret_val = string_concat(num_str, (long long)"LL"); + goto L_cleanup; + } + if (type == 2LL) { + val = get_list(expr, 1LL); + escaped = escape_string(val); + res = (long long)"(long long)\""; + res = string_concat(res, escaped); + res = string_concat(res, (long long)"\""); + ret_val = res; + goto L_cleanup; + } + if (type == 3LL) { + name = get_list(expr, 1LL); + if (map_contains_key(var_keys, name) == 0LL) { + if (map_contains_key(get_list(state, 12LL), name) == 1LL) { + eres = (long long)"({ long long* _v = (long long*)malloc(sizeof(long long) * 1); _v[0] = EP_TAG_"; + eres = string_concat(eres, name); + eres = string_concat(eres, (long long)"; ep_gc_register(_v, EP_OBJ_STRUCT); (long long)_v; })"); + ret_val = eres; + goto L_cleanup; + } + } + if (map_contains_key(var_keys, name) == 0LL) { + fk = get_list(state, 3LL); + if (map_contains_key(fk, name) == 1LL) { + fres = (long long)"(long long)"; + fres = string_concat(fres, name); + ret_val = fres; + goto L_cleanup; + } + } + ret_val = name; + goto L_cleanup; + } + if (type == 4LL) { + left = get_list(expr, 1LL); + op = get_list(expr, 2LL); + right = get_list(expr, 3LL); + left_str = gen_expr(state, left, var_keys, var_values); + right_str = gen_expr(state, right, var_keys, var_values); + op_str = (long long)""; + if (op == 1LL) { + op_str = (long long)"+"; + } + if (op == 2LL) { + op_str = (long long)"-"; + } + if (op == 3LL) { + op_str = (long long)"*"; + } + if (op == 4LL) { + op_str = (long long)"/"; + } + if (op == 5LL) { + op_str = (long long)"%"; + } + res = (long long)"("; + res = string_concat(res, left_str); + res = string_concat(res, (long long)" "); + res = string_concat(res, op_str); + res = string_concat(res, (long long)" "); + res = string_concat(res, right_str); + res = string_concat(res, (long long)")"); + ret_val = res; + goto L_cleanup; + } + if (type == 5LL) { + left = get_list(expr, 1LL); + op = get_list(expr, 2LL); + right = get_list(expr, 3LL); + left_str = gen_expr(state, left, var_keys, var_values); + right_str = gen_expr(state, right, var_keys, var_values); + lt = infer_type(state, left, var_keys, var_values); + is_string = 0LL; + if ((lt == 2LL || lt == 3LL)) { + is_string = 1LL; + } + if (is_string == 1LL) { + cmp_op = (long long)""; + if (op == 1LL) { + cmp_op = (long long)"< 0"; + } + if (op == 2LL) { + cmp_op = (long long)"> 0"; + } + if (op == 3LL) { + cmp_op = (long long)"== 0"; + } + if (op == 4LL) { + cmp_op = (long long)"!= 0"; + } + if (op == 5LL) { + cmp_op = (long long)"<= 0"; + } + if (op == 6LL) { + cmp_op = (long long)">= 0"; + } + res = (long long)"(strcmp((char*)"; + res = string_concat(res, left_str); + res = string_concat(res, (long long)", (char*)"); + res = string_concat(res, right_str); + res = string_concat(res, (long long)") "); + res = string_concat(res, cmp_op); + res = string_concat(res, (long long)")"); + ret_val = res; + goto L_cleanup; + } else { + op_str = (long long)""; + if (op == 1LL) { + op_str = (long long)"<"; + } + if (op == 2LL) { + op_str = (long long)">"; + } + if (op == 3LL) { + op_str = (long long)"=="; + } + if (op == 4LL) { + op_str = (long long)"!="; + } + if (op == 5LL) { + op_str = (long long)"<="; + } + if (op == 6LL) { + op_str = (long long)">="; + } + res = (long long)""; + res = string_concat(res, left_str); + res = string_concat(res, (long long)" "); + res = string_concat(res, op_str); + res = string_concat(res, (long long)" "); + res = string_concat(res, right_str); + ret_val = res; + goto L_cleanup; + } + } + if (type == 14LL) { + left = get_list(expr, 1LL); + op = get_list(expr, 2LL); + right = get_list(expr, 3LL); + left_str = gen_expr(state, left, var_keys, var_values); + right_str = gen_expr(state, right, var_keys, var_values); + op_str = (long long)""; + if (op == 1LL) { + op_str = (long long)"&&"; + } + if (op == 2LL) { + op_str = (long long)"||"; + } + res = (long long)"("; + res = string_concat(res, left_str); + res = string_concat(res, (long long)" "); + res = string_concat(res, op_str); + res = string_concat(res, (long long)" "); + res = string_concat(res, right_str); + res = string_concat(res, (long long)")"); + ret_val = res; + goto L_cleanup; + } + if (type == 6LL) { + name = get_list(expr, 1LL); + args = get_list(expr, 2LL); + args_len = length_list(args); + { + long long tmp_val = create_list(); + free_list(formatted_args); + formatted_args = tmp_val; + } + idx = 0LL; + while (idx < args_len) { + arg = get_list(args, idx); + arg_val = gen_expr(state, arg, var_keys, var_values); + needs_cast = 0LL; + if ((((((((strcmp((char*)(long long)"read_file_content", (char*)name) == 0) || (strcmp((char*)(long long)"string_length", (char*)name) == 0)) || (strcmp((char*)(long long)"display_string", (char*)name) == 0)) || (strcmp((char*)(long long)"run_command", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_md5", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_sha256", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_net_connect", (char*)name) == 0))) { + if (idx == 0LL) { + needs_cast = 1LL; + } + } else { + if (((strcmp((char*)(long long)"get_character", (char*)name) == 0) || (strcmp((char*)(long long)"substring", (char*)name) == 0))) { + if (idx == 0LL) { + needs_cast = 1LL; + } + } else { + if ((strcmp((char*)(long long)"write_file_content", (char*)name) == 0)) { + if ((idx == 0LL || idx == 1LL)) { + needs_cast = 1LL; + } + } else { + if ((strcmp((char*)(long long)"ep_net_send", (char*)name) == 0)) { + if (idx == 1LL) { + needs_cast = 1LL; + } + } + } + } + } + casted = (long long)""; + if (needs_cast == 1LL) { + casted = string_concat((long long)"(char*)", arg_val); + } else { + casted = arg_val; + } + ok = append_list(formatted_args, casted); + idx = (idx + 1LL); + } + { + long long tmp_val = create_list(); + free_list(args_str_list); + args_str_list = tmp_val; + } + idx = 0LL; + while (idx < args_len) { + arg_item = get_list(formatted_args, idx); + ok = append_list(args_str_list, arg_item); + if (idx < (args_len - 1LL)) { + ok = append_list(args_str_list, (long long)", "); + } + idx = (idx + 1LL); + } + args_joined = join_strings(args_str_list); + func_keys = get_list(state, 3LL); + if (map_contains_key(func_keys, name) == 0LL) { + if (map_contains_key(var_keys, name) == 1LL) { + cl_sig = (long long)"long long(*)(long long"; + rw_sig = (long long)"long long(*)("; + sg_i = 0LL; + while (sg_i < args_len) { + cl_sig = string_concat(cl_sig, (long long)", long long"); + if (sg_i > 0LL) { + rw_sig = string_concat(rw_sig, (long long)", long long"); + } else { + rw_sig = string_concat(rw_sig, (long long)"long long"); + } + sg_i = (sg_i + 1LL); + } + if (args_len == 0LL) { + rw_sig = string_concat(rw_sig, (long long)"void"); + } + cl_sig = string_concat(cl_sig, (long long)")"); + rw_sig = string_concat(rw_sig, (long long)")"); + res = (long long)"({ long long _fv = "; + res = string_concat(res, name); + res = string_concat(res, (long long)"; EpClosure* _cl = (EpClosure*)_fv; (_fv != 0 && _cl->magic == EP_CLOSURE_MAGIC) ? (("); + res = string_concat(res, cl_sig); + res = string_concat(res, (long long)")_cl->fn_ptr)((long long)_cl->env"); + if (args_len > 0LL) { + res = string_concat(res, (long long)", "); + res = string_concat(res, args_joined); + } + res = string_concat(res, (long long)") : (("); + res = string_concat(res, rw_sig); + res = string_concat(res, (long long)")_fv)("); + res = string_concat(res, args_joined); + res = string_concat(res, (long long)"); })"); + ret_val = res; + goto L_cleanup; + } + } + call_str = name; + call_str = string_concat(call_str, (long long)"("); + call_str = string_concat(call_str, args_joined); + call_str = string_concat(call_str, (long long)")"); + if ((((((((strcmp((char*)(long long)"read_file_content", (char*)name) == 0) || (strcmp((char*)(long long)"get_argument", (char*)name) == 0)) || (strcmp((char*)(long long)"substring", (char*)name) == 0)) || (strcmp((char*)(long long)"string_from_list", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_net_recv", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_md5", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_sha256", (char*)name) == 0))) { + res = (long long)"(long long)"; + res = string_concat(res, call_str); + ret_val = res; + goto L_cleanup; + } else { + ret_val = call_str; + goto L_cleanup; + } + } + if (type == 17LL) { + ret_val = (long long)"create_channel()"; + goto L_cleanup; + } + if (type == 18LL) { + chan = get_list(expr, 1LL); + chan_str = gen_expr(state, chan, var_keys, var_values); + res = (long long)"receive_channel("; + res = string_concat(res, chan_str); + res = string_concat(res, (long long)")"); + ret_val = res; + goto L_cleanup; + } + if (type == 20LL) { + inner = get_list(expr, 1LL); + ret_val = gen_expr(state, inner, var_keys, var_values); + goto L_cleanup; + } + if (type == 21LL) { + inner = get_list(expr, 1LL); + inner_str = gen_expr(state, inner, var_keys, var_values); + res = (long long)"({ EpFuture* _fut = (EpFuture*)"; + res = string_concat(res, inner_str); + res = string_concat(res, (long long)"; long long _res = 0; if (_fut) { if (!_fut->completed) { _res = receive_channel(_fut->chan); } else { _res = _fut->value; } } _res; })"); + ret_val = res; + goto L_cleanup; + } + if (type == 22LL) { + obj = get_list(expr, 1LL); + field_name = get_list(expr, 2LL); + obj_str = gen_expr(state, obj, var_keys, var_values); + res = (long long)"((long long*)"; + res = string_concat(res, obj_str); + res = string_concat(res, (long long)")[EP_FIELD_"); + res = string_concat(res, field_name); + res = string_concat(res, (long long)"]"); + ret_val = res; + goto L_cleanup; + } + if (type == 24LL) { + struct_name = get_list(expr, 1LL); + fields = get_list(expr, 2LL); + field_count = length_list(fields); + res = (long long)"({ long long* _s = (long long*)malloc(sizeof(long long) * EP_STRUCT_MAX_SLOTS); "; + f_idx = 0LL; + while (f_idx < field_count) { + fpair = get_list(fields, f_idx); + fname = get_list(fpair, 0LL); + fval = get_list(fpair, 1LL); + fval_str = gen_expr(state, fval, var_keys, var_values); + res = string_concat(res, (long long)"_s[EP_FIELD_"); + res = string_concat(res, fname); + res = string_concat(res, (long long)"] = "); + res = string_concat(res, fval_str); + res = string_concat(res, (long long)"; "); + f_idx = (f_idx + 1LL); + } + res = string_concat(res, (long long)"ep_gc_register(_s, EP_OBJ_STRUCT); (long long)_s; })"); + ret_val = res; + goto L_cleanup; + } + if (type == 25LL) { + obj = get_list(expr, 1LL); + method_name = get_list(expr, 2LL); + args = get_list(expr, 3LL); + obj_str = gen_expr(state, obj, var_keys, var_values); + args_len = length_list(args); + res = method_name; + res = string_concat(res, (long long)"("); + res = string_concat(res, obj_str); + idx = 0LL; + while (idx < args_len) { + arg = get_list(args, idx); + arg_str = gen_expr(state, arg, var_keys, var_values); + res = string_concat(res, (long long)", "); + res = string_concat(res, arg_str); + idx = (idx + 1LL); + } + res = string_concat(res, (long long)")"); + ret_val = res; + goto L_cleanup; + } + if (type == 26LL) { + variant_name = get_list(expr, 1LL); + args = get_list(expr, 2LL); + args_len = length_list(args); + alloc_size = (args_len + 1LL); + res = (long long)"({ long long* _v = (long long*)malloc(sizeof(long long) * "; + res = string_concat(res, cg_int_to_str(alloc_size)); + res = string_concat(res, (long long)"); _v[0] = EP_TAG_"); + res = string_concat(res, variant_name); + res = string_concat(res, (long long)"; "); + a_idx = 0LL; + while (a_idx < args_len) { + arg = get_list(args, a_idx); + arg_str = gen_expr(state, arg, var_keys, var_values); + res = string_concat(res, (long long)"_v["); + res = string_concat(res, cg_int_to_str((a_idx + 1LL))); + res = string_concat(res, (long long)"] = "); + res = string_concat(res, arg_str); + res = string_concat(res, (long long)"; "); + a_idx = (a_idx + 1LL); + } + res = string_concat(res, (long long)"ep_gc_register(_v, EP_OBJ_STRUCT); (long long)_v; })"); + ret_val = res; + goto L_cleanup; + } + if (type == 31LL) { + val = get_list(expr, 1LL); + if (val == 1LL) { + ret_val = (long long)"1LL"; + goto L_cleanup; + } + ret_val = (long long)"0LL"; + goto L_cleanup; + } + if (type == 32LL) { + inner = get_list(expr, 1LL); + inner_str = gen_expr(state, inner, var_keys, var_values); + res = (long long)"(!"; + res = string_concat(res, inner_str); + res = string_concat(res, (long long)")"); + ret_val = res; + goto L_cleanup; + } + if (type == 33LL) { + inner = get_list(expr, 1LL); + inner_str = gen_expr(state, inner, var_keys, var_values); + ret_val = inner_str; + goto L_cleanup; + } + if (type == 34LL) { + params = get_list(expr, 1LL); + body = get_list(expr, 2LL); + cidx = get_codegen_spawn_index(state); + dummy = set_codegen_spawn_index(state, (cidx + 1LL)); + cname = string_concat((long long)"_ep_closure_", cg_int_to_str(cidx)); + { + long long tmp_val = create_list(); + free_list(raw_names); + raw_names = tmp_val; + } + okr = collect_idents_stmts(body, raw_names); + func_keys = get_list(state, 3LL); + p_len = length_list(params); + { + long long tmp_val = create_list(); + free_list(captured); + captured = tmp_val; + } + rn_len = length_list(raw_names); + rn_i = 0LL; + while (rn_i < rn_len) { + nm = string_concat(get_list(raw_names, rn_i), (long long)""); + skip = 0LL; + if ((strcmp((char*)nm, (char*)(long long)"ret_val") == 0)) { + skip = 1LL; + } + if (skip == 0LL) { + if (map_contains_key(captured, nm) == 1LL) { + skip = 1LL; + } + } + if (skip == 0LL) { + pp_i = 0LL; + while (pp_i < p_len) { + p_node = get_list(params, pp_i); + if ((strcmp((char*)nm, (char*)get_list(p_node, 0LL)) == 0)) { + skip = 1LL; + } + pp_i = (pp_i + 1LL); + } + } + if (skip == 0LL) { + if (map_contains_key(var_keys, nm) == 0LL) { + skip = 1LL; + } + } + if (skip == 0LL) { + if (map_contains_key(func_keys, nm) == 1LL) { + skip = 1LL; + } + } + if (skip == 0LL) { + okc = append_list(captured, nm); + } + rn_i = (rn_i + 1LL); + } + n_caps = length_list(captured); + saved_lines = get_list(state, 0LL); + fresh = (create_list() + 0LL); + dummy2 = set_list(state, 0LL, fresh); + hdr = (long long)"long long "; + hdr = string_concat(hdr, cname); + hdr = string_concat(hdr, (long long)"(long long _ep_env"); + hp_i = 0LL; + while (hp_i < p_len) { + p_node = get_list(params, hp_i); + hdr = string_concat(hdr, (long long)", long long "); + hdr = string_concat(hdr, get_list(p_node, 0LL)); + hp_i = (hp_i + 1LL); + } + hdr = string_concat(hdr, (long long)") {\n long long ret_val = 0;\n"); + okh = emit(state, hdr); + cp_i = 0LL; + while (cp_i < n_caps) { + unp = (long long)" long long "; + unp = string_concat(unp, get_list(captured, cp_i)); + unp = string_concat(unp, (long long)" = ((long long*)_ep_env)["); + unp = string_concat(unp, cg_int_to_str(cp_i)); + unp = string_concat(unp, (long long)"];\n"); + oku = emit(state, unp); + cp_i = (cp_i + 1LL); + } + { + long long tmp_val = create_list(); + free_list(c_keys); + c_keys = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(c_values); + c_values = tmp_val; + } + ov_len = length_list(var_keys); + ov_i = 0LL; + while (ov_i < ov_len) { + okov = map_put(c_keys, c_values, get_list(var_keys, ov_i), get_list(var_values, ov_i)); + ov_i = (ov_i + 1LL); + } + pv_i = 0LL; + while (pv_i < p_len) { + p_node = get_list(params, pv_i); + okpv = map_put(c_keys, c_values, get_list(p_node, 0LL), 1LL); + pv_i = (pv_i + 1LL); + } + { + long long tmp_val = create_list(); + free_list(b_keys); + b_keys = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(b_values); + b_values = tmp_val; + } + okbv = collect_var_types(state, body, b_keys, b_values); + bv_len = length_list(b_keys); + bv_i = 0LL; + while (bv_i < bv_len) { + bname = string_concat(get_list(b_keys, bv_i), (long long)""); + is_p = 0LL; + bp_i = 0LL; + while (bp_i < p_len) { + p_node = get_list(params, bp_i); + if ((strcmp((char*)bname, (char*)get_list(p_node, 0LL)) == 0)) { + is_p = 1LL; + } + bp_i = (bp_i + 1LL); + } + if (is_p == 0LL) { + if (map_contains_key(captured, bname) == 0LL) { + if ((strcmp((char*)bname, (char*)(long long)"ret_val") == 0)) { + is_p = 1LL; + } else { + dec = (long long)" long long "; + dec = string_concat(dec, bname); + dec = string_concat(dec, (long long)" = 0;\n"); + okd = emit(state, dec); + } + } + } + okbm = map_put(c_keys, c_values, bname, get_list(b_values, bv_i)); + bv_i = (bv_i + 1LL); + } + bs_len = length_list(body); + bs_i = 0LL; + while (bs_i < bs_len) { + okst = gen_statement(state, get_list(body, bs_i), c_keys, c_values); + bs_i = (bs_i + 1LL); + } + okft = emit(state, (long long)"L_cleanup:\n return ret_val;\n}\n\n"); + closure_text = join_strings(fresh); + dummy3 = set_list(state, 0LL, saved_lines); + cbodies = get_list(state, 11LL); + okcb = append_list(cbodies, closure_text); + res = (long long)"({ EpClosure* _cl_"; + res = string_concat(res, cg_int_to_str(cidx)); + res = string_concat(res, (long long)" = (EpClosure*)malloc(sizeof(EpClosure) + "); + res = string_concat(res, cg_int_to_str(n_caps)); + res = string_concat(res, (long long)" * sizeof(long long)); _cl_"); + res = string_concat(res, cg_int_to_str(cidx)); + res = string_concat(res, (long long)"->magic = EP_CLOSURE_MAGIC; _cl_"); + res = string_concat(res, cg_int_to_str(cidx)); + res = string_concat(res, (long long)"->fn_ptr = (long long)"); + res = string_concat(res, cname); + res = string_concat(res, (long long)";"); + ce_i = 0LL; + while (ce_i < n_caps) { + res = string_concat(res, (long long)" _cl_"); + res = string_concat(res, cg_int_to_str(cidx)); + res = string_concat(res, (long long)"->env["); + res = string_concat(res, cg_int_to_str(ce_i)); + res = string_concat(res, (long long)"] = "); + res = string_concat(res, get_list(captured, ce_i)); + res = string_concat(res, (long long)";"); + ce_i = (ce_i + 1LL); + } + res = string_concat(res, (long long)" (long long)_cl_"); + res = string_concat(res, cg_int_to_str(cidx)); + res = string_concat(res, (long long)"; })"); + ret_val = res; + goto L_cleanup; + } + if (type == 35LL) { + elements = get_list(expr, 1LL); + elem_len = length_list(elements); + res = (long long)"({ long long _lst = create_list(); "; + e_idx = 0LL; + while (e_idx < elem_len) { + elem = get_list(elements, e_idx); + elem_str = gen_expr(state, elem, var_keys, var_values); + res = string_concat(res, (long long)"append_list(_lst, "); + res = string_concat(res, elem_str); + res = string_concat(res, (long long)"); "); + e_idx = (e_idx + 1LL); + } + res = string_concat(res, (long long)"_lst; })"); + ret_val = res; + goto L_cleanup; + } + ret_val = (long long)""; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(33); + free_list(formatted_args); + free_list(args_str_list); + free_list(raw_names); + free_list(captured); + free_list(c_keys); + free_list(c_values); + free_list(b_keys); + free_list(b_values); + return ret_val; +} + +long long get_c_runtime_source() { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = get_shared_runtime_source(); + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long get_c_main_source() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"\n/* Bootstrapper C main */\n"); + ok = append_list(lines, (long long)"int main(int argc, char** argv) {\n"); + ok = append_list(lines, (long long)" init_ep_args(argc, argv);\n"); + ok = append_list(lines, (long long)" int result = (int)_main();\n"); + ok = append_list(lines, (long long)" ep_gc_shutdown();\n"); + ok = append_list(lines, (long long)" return result;\n"); + ok = append_list(lines, (long long)"}\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long get_c_test_main_source(long long program) { + long long funcs = 0; + long long funcs_len = 0; + long long test_cases = 0; + long long idx = 0; + long long func = 0; + long long name = 0; + long long name_len = 0; + long long prefix = 0; + long long ok = 0; + long long test_count = 0; + long long lines = 0; + long long idx2 = 0; + long long tc_name = 0; + long long ret_val = 0; + + ep_gc_push_root(&test_cases); + ep_gc_push_root(&prefix); + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + funcs = get_list(program, 3LL); + funcs_len = length_list(funcs); + { + long long tmp_val = create_list(); + free_list(test_cases); + test_cases = tmp_val; + } + idx = 0LL; + while (idx < funcs_len) { + func = get_list(funcs, idx); + name = get_list(func, 1LL); + name_len = string_length((char*)name); + if (name_len > 4LL) { + prefix = (long long)substring((char*)name, 0LL, 5LL); + if ((strcmp((char*)(long long)"test_", (char*)prefix) == 0)) { + ok = append_list(test_cases, name); + } + } + idx = (idx + 1LL); + } + test_count = length_list(test_cases); + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"\n/* Test runner C main */\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n\n"); + ok = append_list(lines, (long long)"int run_test(long long (*test_func)(void), const char* name) {\n"); + ok = append_list(lines, (long long)" printf(\"test_%s ... \", name);\n"); + ok = append_list(lines, (long long)" fflush(stdout);\n"); + ok = append_list(lines, (long long)" pid_t pid = fork();\n"); + ok = append_list(lines, (long long)" if (pid < 0) {\n"); + ok = append_list(lines, (long long)" printf(\"FAILED (fork failed)\\n\");\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (pid == 0) {\n"); + ok = append_list(lines, (long long)" exit((int)test_func());\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" int status;\n"); + ok = append_list(lines, (long long)" waitpid(pid, &status, 0);\n"); + ok = append_list(lines, (long long)" if (WIFEXITED(status)) {\n"); + ok = append_list(lines, (long long)" int exit_code = WEXITSTATUS(status);\n"); + ok = append_list(lines, (long long)" if (exit_code == 0) {\n"); + ok = append_list(lines, (long long)" printf(\"OK\\n\");\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" printf(\"FAILED (exit code %d)\\n\", exit_code);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (WIFSIGNALED(status)) {\n"); + ok = append_list(lines, (long long)" int sig = WTERMSIG(status);\n"); + ok = append_list(lines, (long long)" printf(\"FAILED (crashed/signal %d)\\n\", sig);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" printf(\"FAILED\\n\");\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n\n"); + ok = append_list(lines, (long long)"int main(int argc, char** argv) {\n"); + ok = append_list(lines, (long long)" init_ep_args(argc, argv);\n"); + ok = append_list(lines, (long long)" printf(\"Running "); + ok = append_list(lines, cg_int_to_str(test_count)); + ok = append_list(lines, (long long)" tests...\\n\");\n"); + ok = append_list(lines, (long long)" int passed = 0;\n"); + ok = append_list(lines, (long long)" int failed = 0;\n"); + ok = append_list(lines, (long long)" int total = 0;\n\n"); + idx2 = 0LL; + while (idx2 < test_count) { + tc_name = get_list(test_cases, idx2); + ok = append_list(lines, (long long)" total++;\n"); + ok = append_list(lines, (long long)" if (run_test("); + ok = append_list(lines, tc_name); + ok = append_list(lines, (long long)", \""); + ok = append_list(lines, tc_name); + ok = append_list(lines, (long long)"\")) passed++; else failed++;\n"); + idx2 = (idx2 + 1LL); + } + ok = append_list(lines, (long long)"\n printf(\"\\nResult: %d passed; %d failed\\n\", passed, failed);\n"); + ok = append_list(lines, (long long)" if (failed > 0) return 1;\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(3); + free_list(test_cases); + free_list(lines); + return ret_val; +} + +long long collect_spawns_in_stmts(long long stmts, long long spawn_list) { + long long len = 0; + long long idx = 0; + long long stmt = 0; + long long type = 0; + long long ok = 0; + long long then_b = 0; + long long else_b = 0; + long long body = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + len = length_list(stmts); + idx = 0LL; + while (idx < len) { + stmt = get_list(stmts, idx); + type = get_list(stmt, 0LL); + if (type == 15LL) { + ok = append_list(spawn_list, stmt); + } else { + if (type == 10LL) { + then_b = get_list(stmt, 2LL); + else_b = get_list(stmt, 3LL); + ok = collect_spawns_in_stmts(then_b, spawn_list); + if (else_b != 0LL) { + ok = collect_spawns_in_stmts(else_b, spawn_list); + } + } else { + if (type == 11LL) { + body = get_list(stmt, 2LL); + ok = collect_spawns_in_stmts(body, spawn_list); + } + } + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long collect_all_spawns(long long program) { + long long spawn_list = 0; + long long funcs = 0; + long long len = 0; + long long idx = 0; + long long func = 0; + long long body = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&spawn_list); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(spawn_list); + spawn_list = tmp_val; + } + funcs = get_list(program, 3LL); + len = length_list(funcs); + idx = 0LL; + while (idx < len) { + func = get_list(funcs, idx); + body = get_list(func, 3LL); + ok = collect_spawns_in_stmts(body, spawn_list); + idx = (idx + 1LL); + } + ret_val = spawn_list; + spawn_list = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(spawn_list); + return ret_val; +} + +long long clone_list(long long lst) { + long long new_lst = 0; + long long len = 0; + long long idx = 0; + long long item = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&new_lst); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(new_lst); + new_lst = tmp_val; + } + len = length_list(lst); + idx = 0LL; + while (idx < len) { + item = get_list(lst, idx); + ok = append_list(new_lst, item); + idx = (idx + 1LL); + } + ret_val = new_lst; + new_lst = 0; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(new_lst); + return ret_val; +} + +long long check_expr_reads(long long expr, long long var_keys, long long var_values, long long state_keys, long long state_values) { + long long type = 0; + long long name = 0; + long long t = 0; + long long is_tracked = 0; + long long st = 0; + long long ok = 0; + long long inner = 0; + long long left = 0; + long long right = 0; + long long args = 0; + long long args_len = 0; + long long idx = 0; + long long arg = 0; + long long chan = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + type = get_list(expr, 0LL); + if (((type == 1LL || type == 2LL) || type == 17LL)) { + ret_val = 1LL; + goto L_cleanup; + } + if (type == 3LL) { + name = get_list(expr, 1LL); + t = map_get(var_keys, var_values, name); + is_tracked = 0LL; + if (((((t == 4LL || t == 2LL) || t == 3LL) || t == 5LL) || t == 6LL)) { + is_tracked = 1LL; + } + if (is_tracked == 1LL) { + st = map_get(state_keys, state_values, name); + if (st == 2LL) { + printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:"); + ok = display_string((char*)name); + ret_val = 0LL; + goto L_cleanup; + } + } + ret_val = 1LL; + goto L_cleanup; + } + if (type == 20LL) { + inner = get_list(expr, 1LL); + ret_val = check_expr_reads(inner, var_keys, var_values, state_keys, state_values); + goto L_cleanup; + } + if (type == 21LL) { + inner = get_list(expr, 1LL); + ret_val = check_expr_reads(inner, var_keys, var_values, state_keys, state_values); + goto L_cleanup; + } + if (((type == 4LL || type == 5LL) || type == 14LL)) { + left = get_list(expr, 1LL); + right = get_list(expr, 3LL); + ok = check_expr_reads(left, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + ok = check_expr_reads(right, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + ret_val = 1LL; + goto L_cleanup; + } + if (type == 6LL) { + name = get_list(expr, 1LL); + args = get_list(expr, 2LL); + args_len = length_list(args); + idx = 0LL; + while (idx < args_len) { + arg = get_list(args, idx); + ok = check_expr_reads(arg, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ret_val = 1LL; + goto L_cleanup; + } + if (type == 18LL) { + chan = get_list(expr, 1LL); + ret_val = check_expr_reads(chan, var_keys, var_values, state_keys, state_values); + goto L_cleanup; + } + ret_val = 1LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long dec_borrow_count(long long target, long long count_keys, long long count_values) { + long long val = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + val = map_get(count_keys, count_values, target); + if (val != 0LL) { + if (val > 0LL) { + ok = map_put(count_keys, count_values, target, (val - 1LL)); + } + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long inc_borrow_count(long long target, long long count_keys, long long count_values) { + long long val = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + val = map_get(count_keys, count_values, target); + ok = map_put(count_keys, count_values, target, (val + 1LL)); + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long check_safety_stmts(long long func, long long stmts, long long var_keys, long long var_values, long long state_keys, long long state_values, long long borrow_keys, long long borrow_values, long long count_keys, long long count_values) { + long long len = 0; + long long idx = 0; + long long stmt = 0; + long long type = 0; + long long name = 0; + long long expr = 0; + long long ok = 0; + long long old_target = 0; + long long expr_type = 0; + long long inner = 0; + long long inner_type = 0; + long long target = 0; + long long st = 0; + long long bc = 0; + long long t = 0; + long long is_tracked = 0; + long long src = 0; + long long src_t = 0; + long long src_tracked = 0; + long long src_bc = 0; + long long chan = 0; + long long val = 0; + long long val_type = 0; + long long func_name = 0; + long long args = 0; + long long args_len = 0; + long long a_idx = 0; + long long arg = 0; + long long arg_type = 0; + long long params = 0; + long long p_len = 0; + long long p_idx = 0; + long long is_borrowed_param = 0; + long long p_node = 0; + long long p_name = 0; + long long is_borrow = 0; + long long cond = 0; + long long then_b = 0; + long long else_b = 0; + long long then_state_keys = 0; + long long then_state_values = 0; + long long then_borrow_keys = 0; + long long then_borrow_values = 0; + long long then_count_keys = 0; + long long then_count_values = 0; + long long else_state_keys = 0; + long long else_state_values = 0; + long long else_borrow_keys = 0; + long long else_borrow_values = 0; + long long else_count_keys = 0; + long long else_count_values = 0; + long long st_len = 0; + long long st_idx = 0; + long long var_name = 0; + long long then_val = 0; + long long else_val = 0; + long long b_len = 0; + long long b_idx = 0; + long long b_k = 0; + long long b_v = 0; + long long bc_len = 0; + long long bc_idx = 0; + long long bc_k = 0; + long long bc_v = 0; + long long cur_v = 0; + long long body = 0; + long long start_state_keys = 0; + long long start_state_values = 0; + long long start_val = 0; + long long end_val = 0; + long long ret_val = 0; + + ep_gc_push_root(&then_state_keys); + ep_gc_push_root(&then_state_values); + ep_gc_push_root(&then_borrow_keys); + ep_gc_push_root(&then_borrow_values); + ep_gc_push_root(&then_count_keys); + ep_gc_push_root(&then_count_values); + ep_gc_push_root(&else_state_keys); + ep_gc_push_root(&else_state_values); + ep_gc_push_root(&else_borrow_keys); + ep_gc_push_root(&else_borrow_values); + ep_gc_push_root(&else_count_keys); + ep_gc_push_root(&else_count_values); + ep_gc_push_root(&start_state_keys); + ep_gc_push_root(&start_state_values); + ep_gc_maybe_collect(); + + len = length_list(stmts); + idx = 0LL; + while (idx < len) { + stmt = get_list(stmts, idx); + type = get_list(stmt, 0LL); + if (type == 7LL) { + name = get_list(stmt, 1LL); + expr = get_list(stmt, 2LL); + ok = check_expr_reads(expr, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + old_target = map_get(borrow_keys, borrow_values, name); + if (old_target != 0LL) { + ok = map_put(borrow_keys, borrow_values, name, 0LL); + ok = dec_borrow_count(old_target, count_keys, count_values); + } + expr_type = get_list(expr, 0LL); + if (expr_type == 20LL) { + inner = get_list(expr, 1LL); + inner_type = get_list(inner, 0LL); + if (inner_type == 3LL) { + target = get_list(inner, 1LL); + st = map_get(state_keys, state_values, target); + if (st == 2LL) { + printf("%s\n", (char*)(long long)"Safety Error: Cannot borrow moved variable:"); + ok = display_string((char*)target); + ret_val = 0LL; + goto L_cleanup; + } + ok = map_put(borrow_keys, borrow_values, name, target); + ok = inc_borrow_count(target, count_keys, count_values); + } else { + printf("%s\n", (char*)(long long)"Safety Error: Expected identifier in borrow expression"); + ret_val = 0LL; + goto L_cleanup; + } + } else { + bc = map_get(count_keys, count_values, name); + if (bc > 0LL) { + printf("%s\n", (char*)(long long)"Safety Error: Cannot modify variable because it is currently borrowed:"); + ok = display_string((char*)name); + ret_val = 0LL; + goto L_cleanup; + } + t = map_get(var_keys, var_values, name); + is_tracked = 0LL; + if (((((t == 4LL || t == 2LL) || t == 3LL) || t == 5LL) || t == 6LL)) { + is_tracked = 1LL; + } + if (is_tracked == 1LL) { + if (expr_type == 3LL) { + src = get_list(expr, 1LL); + src_t = map_get(var_keys, var_values, src); + src_tracked = 0LL; + if (((((src_t == 4LL || src_t == 2LL) || src_t == 3LL) || src_t == 5LL) || src_t == 6LL)) { + src_tracked = 1LL; + } + if (src_tracked == 1LL) { + st = map_get(state_keys, state_values, src); + if (st == 2LL) { + printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:"); + ok = display_string((char*)src); + ret_val = 0LL; + goto L_cleanup; + } + src_bc = map_get(count_keys, count_values, src); + if (src_bc > 0LL) { + printf("%s\n", (char*)(long long)"Safety Error: Cannot move variable because it is currently borrowed:"); + ok = display_string((char*)src); + ret_val = 0LL; + goto L_cleanup; + } + if ((src_t != 5LL && src_t != 6LL)) { + ok = map_put(state_keys, state_values, src, 2LL); + } + } + } + ok = map_put(state_keys, state_values, name, 1LL); + } + } + } + if (type == 16LL) { + chan = get_list(stmt, 1LL); + val = get_list(stmt, 2LL); + ok = check_expr_reads(chan, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + val_type = get_list(val, 0LL); + if (val_type == 3LL) { + src = get_list(val, 1LL); + src_t = map_get(var_keys, var_values, src); + src_tracked = 0LL; + if (((((src_t == 4LL || src_t == 2LL) || src_t == 3LL) || src_t == 5LL) || src_t == 6LL)) { + src_tracked = 1LL; + } + if (src_tracked == 1LL) { + st = map_get(state_keys, state_values, src); + if (st == 2LL) { + printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:"); + ok = display_string((char*)src); + ret_val = 0LL; + goto L_cleanup; + } + src_bc = map_get(count_keys, count_values, src); + if (src_bc > 0LL) { + printf("%s\n", (char*)(long long)"Safety Error: Cannot move variable because it is currently borrowed:"); + ok = display_string((char*)src); + ret_val = 0LL; + goto L_cleanup; + } + if ((src_t != 5LL && src_t != 6LL)) { + ok = map_put(state_keys, state_values, src, 2LL); + } + } else { + ok = check_expr_reads(val, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + } else { + ok = check_expr_reads(val, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + } + if (type == 15LL) { + func_name = get_list(stmt, 1LL); + args = get_list(stmt, 2LL); + args_len = length_list(args); + a_idx = 0LL; + while (a_idx < args_len) { + arg = get_list(args, a_idx); + arg_type = get_list(arg, 0LL); + if (arg_type == 3LL) { + src = get_list(arg, 1LL); + src_t = map_get(var_keys, var_values, src); + src_tracked = 0LL; + if (((((src_t == 4LL || src_t == 2LL) || src_t == 3LL) || src_t == 5LL) || src_t == 6LL)) { + src_tracked = 1LL; + } + if (src_tracked == 1LL) { + st = map_get(state_keys, state_values, src); + if (st == 2LL) { + printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:"); + ok = display_string((char*)src); + ret_val = 0LL; + goto L_cleanup; + } + src_bc = map_get(count_keys, count_values, src); + if (src_bc > 0LL) { + printf("%s\n", (char*)(long long)"Safety Error: Cannot move variable because it is currently borrowed:"); + ok = display_string((char*)src); + ret_val = 0LL; + goto L_cleanup; + } + if ((src_t != 5LL && src_t != 6LL)) { + ok = map_put(state_keys, state_values, src, 2LL); + } + } else { + ok = check_expr_reads(arg, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + } else { + ok = check_expr_reads(arg, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + a_idx = (a_idx + 1LL); + } + } + if (type == 9LL) { + expr = get_list(stmt, 1LL); + ok = check_expr_reads(expr, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + if (type == 8LL) { + expr = get_list(stmt, 1LL); + ok = check_expr_reads(expr, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + expr_type = get_list(expr, 0LL); + if (expr_type == 20LL) { + inner = get_list(expr, 1LL); + inner_type = get_list(inner, 0LL); + if (inner_type == 3LL) { + target = get_list(inner, 1LL); + params = get_list(func, 2LL); + p_len = length_list(params); + p_idx = 0LL; + is_borrowed_param = 0LL; + while (p_idx < p_len) { + p_node = get_list(params, p_idx); + p_name = get_list(p_node, 0LL); + is_borrow = get_list(p_node, 1LL); + if ((strcmp((char*)string_concat(p_name, (long long)""), (char*)string_concat(target, (long long)"")) == 0)) { + if (is_borrow == 1LL) { + is_borrowed_param = 1LL; + } + } + p_idx = (p_idx + 1LL); + } + if (is_borrowed_param == 0LL) { + printf("%s\n", (char*)(long long)"Safety Error: Cannot return reference to local variable:"); + ok = display_string((char*)target); + ret_val = 0LL; + goto L_cleanup; + } + } + } else { + if (expr_type == 3LL) { + name = get_list(expr, 1LL); + t = map_get(var_keys, var_values, name); + if ((t == 5LL || t == 6LL)) { + target = map_get(borrow_keys, borrow_values, name); + if (target == 0LL) { + target = name; + } + params = get_list(func, 2LL); + p_len = length_list(params); + p_idx = 0LL; + is_borrowed_param = 0LL; + while (p_idx < p_len) { + p_node = get_list(params, p_idx); + p_name = get_list(p_node, 0LL); + is_borrow = get_list(p_node, 1LL); + if ((strcmp((char*)string_concat(p_name, (long long)""), (char*)string_concat(target, (long long)"")) == 0)) { + if (is_borrow == 1LL) { + is_borrowed_param = 1LL; + } + } + p_idx = (p_idx + 1LL); + } + if (is_borrowed_param == 0LL) { + printf("%s\n", (char*)(long long)"Safety Error: Cannot return reference to local variable:"); + ok = display_string((char*)target); + ret_val = 0LL; + goto L_cleanup; + } + } + } + } + } + if (type == 10LL) { + cond = get_list(stmt, 1LL); + then_b = get_list(stmt, 2LL); + else_b = get_list(stmt, 3LL); + ok = check_expr_reads(cond, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + { + long long tmp_val = clone_list(state_keys); + free_list(then_state_keys); + then_state_keys = tmp_val; + } + { + long long tmp_val = clone_list(state_values); + free_list(then_state_values); + then_state_values = tmp_val; + } + { + long long tmp_val = clone_list(borrow_keys); + free_list(then_borrow_keys); + then_borrow_keys = tmp_val; + } + { + long long tmp_val = clone_list(borrow_values); + free_list(then_borrow_values); + then_borrow_values = tmp_val; + } + { + long long tmp_val = clone_list(count_keys); + free_list(then_count_keys); + then_count_keys = tmp_val; + } + { + long long tmp_val = clone_list(count_values); + free_list(then_count_values); + then_count_values = tmp_val; + } + ok = check_safety_stmts(func, then_b, var_keys, var_values, then_state_keys, then_state_values, then_borrow_keys, then_borrow_values, then_count_keys, then_count_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + { + long long tmp_val = clone_list(state_keys); + free_list(else_state_keys); + else_state_keys = tmp_val; + } + { + long long tmp_val = clone_list(state_values); + free_list(else_state_values); + else_state_values = tmp_val; + } + { + long long tmp_val = clone_list(borrow_keys); + free_list(else_borrow_keys); + else_borrow_keys = tmp_val; + } + { + long long tmp_val = clone_list(borrow_values); + free_list(else_borrow_values); + else_borrow_values = tmp_val; + } + { + long long tmp_val = clone_list(count_keys); + free_list(else_count_keys); + else_count_keys = tmp_val; + } + { + long long tmp_val = clone_list(count_values); + free_list(else_count_values); + else_count_values = tmp_val; + } + if (else_b != 0LL) { + ok = check_safety_stmts(func, else_b, var_keys, var_values, else_state_keys, else_state_values, else_borrow_keys, else_borrow_values, else_count_keys, else_count_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + st_len = length_list(state_keys); + st_idx = 0LL; + while (st_idx < st_len) { + var_name = get_list(state_keys, st_idx); + then_val = map_get(then_state_keys, then_state_values, var_name); + else_val = map_get(else_state_keys, else_state_values, var_name); + if ((then_val == 2LL || else_val == 2LL)) { + ok = map_put(state_keys, state_values, var_name, 2LL); + } + st_idx = (st_idx + 1LL); + } + b_len = length_list(then_borrow_keys); + b_idx = 0LL; + while (b_idx < b_len) { + b_k = get_list(then_borrow_keys, b_idx); + b_v = get_list(then_borrow_values, b_idx); + ok = map_put(borrow_keys, borrow_values, b_k, b_v); + b_idx = (b_idx + 1LL); + } + b_len = length_list(else_borrow_keys); + b_idx = 0LL; + while (b_idx < b_len) { + b_k = get_list(else_borrow_keys, b_idx); + b_v = get_list(else_borrow_values, b_idx); + ok = map_put(borrow_keys, borrow_values, b_k, b_v); + b_idx = (b_idx + 1LL); + } + bc_len = length_list(then_count_keys); + bc_idx = 0LL; + while (bc_idx < bc_len) { + bc_k = get_list(then_count_keys, bc_idx); + bc_v = get_list(then_count_values, bc_idx); + cur_v = map_get(count_keys, count_values, bc_k); + if (bc_v > cur_v) { + ok = map_put(count_keys, count_values, bc_k, bc_v); + } + bc_idx = (bc_idx + 1LL); + } + bc_len = length_list(else_count_keys); + bc_idx = 0LL; + while (bc_idx < bc_len) { + bc_k = get_list(else_count_keys, bc_idx); + bc_v = get_list(else_count_values, bc_idx); + cur_v = map_get(count_keys, count_values, bc_k); + if (bc_v > cur_v) { + ok = map_put(count_keys, count_values, bc_k, bc_v); + } + bc_idx = (bc_idx + 1LL); + } + } + if (type == 11LL) { + cond = get_list(stmt, 1LL); + body = get_list(stmt, 2LL); + ok = check_expr_reads(cond, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + { + long long tmp_val = clone_list(state_keys); + free_list(start_state_keys); + start_state_keys = tmp_val; + } + { + long long tmp_val = clone_list(state_values); + free_list(start_state_values); + start_state_values = tmp_val; + } + ok = check_safety_stmts(func, body, var_keys, var_values, state_keys, state_values, borrow_keys, borrow_values, count_keys, count_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + st_len = length_list(start_state_keys); + st_idx = 0LL; + while (st_idx < st_len) { + var_name = get_list(start_state_keys, st_idx); + start_val = get_list(start_state_values, st_idx); + end_val = map_get(state_keys, state_values, var_name); + if ((start_val == 1LL && end_val == 2LL)) { + printf("%s\n", (char*)(long long)"Safety Error: Variable is moved inside a loop and not reinitialized:"); + ok = display_string((char*)var_name); + ret_val = 0LL; + goto L_cleanup; + } + st_idx = (st_idx + 1LL); + } + ok = check_expr_reads(cond, var_keys, var_values, state_keys, state_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + idx = (idx + 1LL); + } + ret_val = 1LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(14); + free_list(then_state_keys); + free_list(then_state_values); + free_list(then_borrow_keys); + free_list(then_borrow_values); + free_list(then_count_keys); + free_list(then_count_values); + free_list(else_state_keys); + free_list(else_state_values); + free_list(else_borrow_keys); + free_list(else_borrow_values); + free_list(else_count_keys); + free_list(else_count_values); + free_list(start_state_keys); + free_list(start_state_values); + return ret_val; +} + +long long analyze_safety(long long state, long long program) { + long long funcs = 0; + long long funcs_len = 0; + long long idx = 0; + long long func = 0; + long long name = 0; + long long params = 0; + long long body = 0; + long long var_keys = 0; + long long var_values = 0; + long long p_len = 0; + long long p_idx = 0; + long long p_node = 0; + long long p_name = 0; + long long is_borrow = 0; + long long param_type = 0; + long long ok = 0; + long long state_keys = 0; + long long state_values = 0; + long long t = 0; + long long borrow_keys = 0; + long long borrow_values = 0; + long long count_keys = 0; + long long count_values = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + funcs = get_list(program, 3LL); + funcs_len = length_list(funcs); + idx = 0LL; + while (idx < funcs_len) { + func = get_list(funcs, idx); + name = get_list(func, 1LL); + params = get_list(func, 2LL); + body = get_list(func, 3LL); + var_keys = (create_list() + 0LL); + var_values = (create_list() + 0LL); + p_len = length_list(params); + p_idx = 0LL; + while (p_idx < p_len) { + p_node = get_list(params, p_idx); + p_name = get_list(p_node, 0LL); + is_borrow = get_list(p_node, 1LL); + param_type = 1LL; + if (is_borrow == 1LL) { + param_type = 5LL; + } + ok = map_put(var_keys, var_values, p_name, param_type); + p_idx = (p_idx + 1LL); + } + ok = collect_var_types(state, body, var_keys, var_values); + state_keys = (create_list() + 0LL); + state_values = (create_list() + 0LL); + p_idx = 0LL; + while (p_idx < p_len) { + p_node = get_list(params, p_idx); + p_name = get_list(p_node, 0LL); + t = map_get(var_keys, var_values, p_name); + if (((((t == 4LL || t == 2LL) || t == 3LL) || t == 5LL) || t == 6LL)) { + ok = map_put(state_keys, state_values, p_name, 1LL); + } + p_idx = (p_idx + 1LL); + } + borrow_keys = (create_list() + 0LL); + borrow_values = (create_list() + 0LL); + count_keys = (create_list() + 0LL); + count_values = (create_list() + 0LL); + ok = check_safety_stmts(func, body, var_keys, var_values, state_keys, state_values, borrow_keys, borrow_values, count_keys, count_values); + if (ok == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ret_val = 1LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long generate_c(long long program, long long is_test_mode) { + long long state = 0; + long long ok = 0; + long long safety_ok = 0; + long long prog_len = 0; + long long field_slots = 0; + long long struct_defs = 0; + long long sd_len = 0; + long long sd_idx = 0; + long long sdef = 0; + long long sfields = 0; + long long sf_len = 0; + long long sf_idx = 0; + long long sf = 0; + long long sf_name = 0; + long long slot = 0; + long long fs_len = 0; + long long fs_idx = 0; + long long fname = 0; + long long line = 0; + long long slot_line = 0; + long long tag_slots = 0; + long long enum_defs = 0; + long long ed_len = 0; + long long ed_idx = 0; + long long edef = 0; + long long evariants = 0; + long long ev_len = 0; + long long ev_idx = 0; + long long ev = 0; + long long ev_name = 0; + long long tslot = 0; + long long okvn = 0; + long long ts_len = 0; + long long ts_idx = 0; + long long tname = 0; + long long externals = 0; + long long ext_len = 0; + long long idx = 0; + long long ext = 0; + long long name = 0; + long long params = 0; + long long p_len = 0; + long long proto = 0; + long long p_i = 0; + long long funcs = 0; + long long len = 0; + long long func = 0; + long long is_async = 0; + long long proto2 = 0; + long long proto3 = 0; + long long spawn_list = 0; + long long dummy = 0; + long long spawn_len = 0; + long long spawn_node = 0; + long long func_name = 0; + long long args = 0; + long long args_len = 0; + long long struct_decl = 0; + long long j = 0; + long long wrap_fn = 0; + long long c_name = 0; + long long out_lines = 0; + long long closure_slot = 0; + long long method_defs = 0; + long long md_len = 0; + long long md_idx = 0; + long long mdef = 0; + long long mname = 0; + long long msname = 0; + long long mparams = 0; + long long mbody = 0; + long long full_params = 0; + long long self_param = 0; + long long mp_len = 0; + long long mp_idx = 0; + long long mp = 0; + long long method_func = 0; + long long lines = 0; + long long cbodies = 0; + long long marker_line = 0; + long long spliced = 0; + long long oksp = 0; + long long c_code = 0; + long long ret_val = 0; + + ep_gc_push_root(&state); + ep_gc_push_root(&field_slots); + ep_gc_push_root(&line); + ep_gc_push_root(&slot_line); + ep_gc_push_root(&tag_slots); + ep_gc_push_root(&proto); + ep_gc_push_root(&proto2); + ep_gc_push_root(&proto3); + ep_gc_push_root(&spawn_list); + ep_gc_push_root(&struct_decl); + ep_gc_push_root(&wrap_fn); + ep_gc_push_root(&spliced); + ep_gc_push_root(&c_code); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_codegen_state(); + free_list(state); + state = tmp_val; + } + ok = analyze_return_types(state, program); + safety_ok = analyze_safety(state, program); + if (safety_ok == 0LL) { + ret_val = (long long)""; + goto L_cleanup; + } + ok = emit(state, get_c_runtime_source()); + prog_len = length_list(program); + { + long long tmp_val = create_list(); + free_list(field_slots); + field_slots = tmp_val; + } + if (prog_len > 4LL) { + struct_defs = get_list(program, 4LL); + sd_len = length_list(struct_defs); + if (sd_len > 0LL) { + ok = emit(state, (long long)"\n/* Struct Field Index Defines (global slots) */\n"); + sd_idx = 0LL; + while (sd_idx < sd_len) { + sdef = get_list(struct_defs, sd_idx); + sfields = get_list(sdef, 2LL); + sf_len = length_list(sfields); + sf_idx = 0LL; + while (sf_idx < sf_len) { + sf = get_list(sfields, sf_idx); + sf_name = get_list(sf, 0LL); + slot = field_slot_index(field_slots, sf_name); + sf_idx = (sf_idx + 1LL); + } + sd_idx = (sd_idx + 1LL); + } + fs_len = length_list(field_slots); + fs_idx = 0LL; + while (fs_idx < fs_len) { + fname = get_list(field_slots, fs_idx); + line = (long long)"#define EP_FIELD_"; + line = string_concat(line, fname); + line = string_concat(line, (long long)" "); + line = string_concat(line, cg_int_to_str(fs_idx)); + line = string_concat(line, (long long)"\n"); + ok = emit(state, line); + fs_idx = (fs_idx + 1LL); + } + } + } + slot_line = (long long)"#define EP_STRUCT_MAX_SLOTS "; + slot_line = string_concat(slot_line, cg_int_to_str((length_list(field_slots) + 8LL))); + slot_line = string_concat(slot_line, (long long)"\n"); + ok = emit(state, slot_line); + { + long long tmp_val = create_list(); + free_list(tag_slots); + tag_slots = tmp_val; + } + if (prog_len > 5LL) { + enum_defs = get_list(program, 5LL); + ed_len = length_list(enum_defs); + if (ed_len > 0LL) { + ok = emit(state, (long long)"\n/* Enum Tag Defines (global slots) */\n"); + ed_idx = 0LL; + while (ed_idx < ed_len) { + edef = get_list(enum_defs, ed_idx); + evariants = get_list(edef, 2LL); + ev_len = length_list(evariants); + ev_idx = 0LL; + while (ev_idx < ev_len) { + ev = get_list(evariants, ev_idx); + ev_name = get_list(ev, 0LL); + tslot = field_slot_index(tag_slots, ev_name); + okvn = append_list(get_list(state, 12LL), ev_name); + ev_idx = (ev_idx + 1LL); + } + ed_idx = (ed_idx + 1LL); + } + ts_len = length_list(tag_slots); + ts_idx = 0LL; + while (ts_idx < ts_len) { + tname = get_list(tag_slots, ts_idx); + line = (long long)"#define EP_TAG_"; + line = string_concat(line, tname); + line = string_concat(line, (long long)" "); + line = string_concat(line, cg_int_to_str(ts_idx)); + line = string_concat(line, (long long)"\n"); + ok = emit(state, line); + ts_idx = (ts_idx + 1LL); + } + } + } + externals = get_list(program, 2LL); + ext_len = length_list(externals); + ok = emit(state, (long long)"\n/* External Function Prototypes (FFI) */\n"); + idx = 0LL; + while (idx < ext_len) { + ext = get_list(externals, idx); + name = get_list(ext, 1LL); + if (is_builtin_c_func(state, name) == 0LL) { + params = get_list(ext, 2LL); + p_len = length_list(params); + proto = (long long)"long long "; + proto = string_concat(proto, name); + proto = string_concat(proto, (long long)"("); + p_i = 0LL; + while (p_i < p_len) { + proto = string_concat(proto, (long long)"long long"); + if (p_i < (p_len - 1LL)) { + proto = string_concat(proto, (long long)", "); + } + p_i = (p_i + 1LL); + } + proto = string_concat(proto, (long long)");\n"); + ok = emit(state, proto); + } + idx = (idx + 1LL); + } + ok = emit(state, (long long)"\n"); + funcs = get_list(program, 3LL); + len = length_list(funcs); + ok = emit(state, (long long)"\n/* User Function Prototypes */\n"); + idx = 0LL; + while (idx < len) { + func = get_list(funcs, idx); + name = get_list(func, 1LL); + if (is_builtin_c_func(state, name) == 1LL) { + idx = (idx + 1LL); + continue; + } + params = get_list(func, 2LL); + p_len = length_list(params); + is_async = 0LL; + if (length_list(func) > 4LL) { + is_async = get_list(func, 4LL); + } + proto = (long long)"long long "; + proto = string_concat(proto, get_fn_c_name(func)); + proto = string_concat(proto, (long long)"("); + p_i = 0LL; + while (p_i < p_len) { + proto = string_concat(proto, (long long)"long long"); + if (p_i < (p_len - 1LL)) { + proto = string_concat(proto, (long long)", "); + } + p_i = (p_i + 1LL); + } + proto = string_concat(proto, (long long)");\n"); + ok = emit(state, proto); + if (is_async == 1LL) { + proto2 = (long long)"long long "; + proto2 = string_concat(proto2, get_fn_c_name(func)); + proto2 = string_concat(proto2, (long long)"_impl("); + p_i = 0LL; + while (p_i < p_len) { + proto2 = string_concat(proto2, (long long)"long long"); + if (p_i < (p_len - 1LL)) { + proto2 = string_concat(proto2, (long long)", "); + } + p_i = (p_i + 1LL); + } + proto2 = string_concat(proto2, (long long)");\n"); + ok = emit(state, proto2); + proto3 = (long long)"void* "; + proto3 = string_concat(proto3, get_fn_c_name(func)); + proto3 = string_concat(proto3, (long long)"_async_wrapper(void* r);\n"); + ok = emit(state, proto3); + } + idx = (idx + 1LL); + } + ok = emit(state, (long long)"\n"); + { + long long tmp_val = collect_all_spawns(program); + free_list(spawn_list); + spawn_list = tmp_val; + } + dummy = set_codegen_spawn_list(state, spawn_list); + spawn_len = length_list(spawn_list); + ok = emit(state, (long long)"\n/* Thread Spawn Wrappers */\n"); + idx = 0LL; + while (idx < spawn_len) { + spawn_node = get_list(spawn_list, idx); + func_name = get_list(spawn_node, 1LL); + args = get_list(spawn_node, 2LL); + args_len = length_list(args); + struct_decl = (long long)"typedef struct {\n"; + j = 0LL; + while (j < args_len) { + struct_decl = string_concat(struct_decl, (long long)" long long arg"); + struct_decl = string_concat(struct_decl, cg_int_to_str(j)); + struct_decl = string_concat(struct_decl, (long long)";\n"); + j = (j + 1LL); + } + if (args_len == 0LL) { + struct_decl = string_concat(struct_decl, (long long)" int dummy;\n"); + } + struct_decl = string_concat(struct_decl, (long long)"} spawn_args_"); + struct_decl = string_concat(struct_decl, cg_int_to_str(idx)); + struct_decl = string_concat(struct_decl, (long long)";\n\n"); + ok = emit(state, struct_decl); + wrap_fn = (long long)"void* spawn_wrapper_"; + wrap_fn = string_concat(wrap_fn, cg_int_to_str(idx)); + wrap_fn = string_concat(wrap_fn, (long long)"(void* r) {\n"); + wrap_fn = string_concat(wrap_fn, (long long)" spawn_args_"); + wrap_fn = string_concat(wrap_fn, cg_int_to_str(idx)); + wrap_fn = string_concat(wrap_fn, (long long)"* args = (spawn_args_"); + wrap_fn = string_concat(wrap_fn, cg_int_to_str(idx)); + wrap_fn = string_concat(wrap_fn, (long long)"*)r;\n"); + wrap_fn = string_concat(wrap_fn, (long long)" "); + c_name = func_name; + if ((strcmp((char*)(long long)"main", (char*)func_name) == 0)) { + c_name = (long long)"_main"; + } + wrap_fn = string_concat(wrap_fn, c_name); + wrap_fn = string_concat(wrap_fn, (long long)"("); + j = 0LL; + while (j < args_len) { + wrap_fn = string_concat(wrap_fn, (long long)"args->arg"); + wrap_fn = string_concat(wrap_fn, cg_int_to_str(j)); + if (j < (args_len - 1LL)) { + wrap_fn = string_concat(wrap_fn, (long long)", "); + } + j = (j + 1LL); + } + wrap_fn = string_concat(wrap_fn, (long long)");\n"); + wrap_fn = string_concat(wrap_fn, (long long)" free(args);\n"); + wrap_fn = string_concat(wrap_fn, (long long)" return NULL;\n"); + wrap_fn = string_concat(wrap_fn, (long long)"}\n\n"); + ok = emit(state, wrap_fn); + idx = (idx + 1LL); + } + ok = emit(state, (long long)"\n"); + out_lines = get_list(state, 0LL); + closure_slot = length_list(out_lines); + ok = emit(state, (long long)"\n/* EP_CLOSURE_BODIES */\n"); + dummy = set_codegen_spawn_index(state, 0LL); + if (prog_len > 6LL) { + method_defs = get_list(program, 6LL); + md_len = length_list(method_defs); + md_idx = 0LL; + while (md_idx < md_len) { + mdef = get_list(method_defs, md_idx); + mname = get_list(mdef, 1LL); + msname = get_list(mdef, 2LL); + mparams = get_list(mdef, 3LL); + mbody = get_list(mdef, 4LL); + full_params = (create_list() + 0LL); + self_param = (create_list() + 0LL); + ok = append_list(self_param, (long long)"self"); + ok = append_list(self_param, 0LL); + ok = append_list(full_params, self_param); + mp_len = length_list(mparams); + mp_idx = 0LL; + while (mp_idx < mp_len) { + mp = get_list(mparams, mp_idx); + ok = append_list(full_params, mp); + mp_idx = (mp_idx + 1LL); + } + method_func = (make_node_func(mname, full_params, mbody, 0LL) + 0LL); + ok = gen_function(state, method_func); + md_idx = (md_idx + 1LL); + } + } + idx = 0LL; + while (idx < len) { + func = get_list(funcs, idx); + if (is_builtin_c_func(state, get_list(func, 1LL)) == 0LL) { + ok = gen_function(state, func); + } + idx = (idx + 1LL); + } + if (is_test_mode == 1LL) { + ok = emit(state, get_c_test_main_source(program)); + } else { + ok = emit(state, get_c_main_source()); + } + lines = get_list(state, 0LL); + cbodies = get_list(state, 11LL); + if (length_list(cbodies) > 0LL) { + marker_line = get_list(lines, closure_slot); + spliced = string_concat(marker_line, join_strings(cbodies)); + oksp = set_list(lines, closure_slot, spliced); + } + c_code = join_strings(lines); + ret_val = c_code; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(13); + free_list(state); + free_list(field_slots); + free_list(tag_slots); + free_list(spawn_list); + return ret_val; +} + +long long ep_rt_core_0() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)"#define _SETJMP_H\n"); + ok = append_list(lines, (long long)"typedef int jmp_buf[1];\n"); + ok = append_list(lines, (long long)"#define setjmp(buf) (0)\n"); + ok = append_list(lines, (long long)"#define longjmp(buf, val) abort()\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// Mock pthreads for single-threaded WASM\n"); + ok = append_list(lines, (long long)"typedef struct { int lock_state; } pthread_mutex_t;\n"); + ok = append_list(lines, (long long)"typedef struct { int cond_state; } pthread_cond_t;\n"); + ok = append_list(lines, (long long)"typedef struct { int rw_state; } pthread_rwlock_t;\n"); + ok = append_list(lines, (long long)"typedef int pthread_t;\n"); + ok = append_list(lines, (long long)"typedef int pthread_attr_t;\n"); + ok = append_list(lines, (long long)"#define PTHREAD_MUTEX_INITIALIZER {0}\n"); + ok = append_list(lines, (long long)"#define PTHREAD_COND_INITIALIZER {0}\n"); + ok = append_list(lines, (long long)"#define PTHREAD_RWLOCK_INITIALIZER {0}\n"); + ok = append_list(lines, (long long)"#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1)\n"); + ok = append_list(lines, (long long)"#define pthread_mutex_destroy(m) ((void)(m), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_cond_signal(c) ((void)(c), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_cond_broadcast(c) ((void)(c), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_cond_destroy(c) ((void)(c), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0)\n"); + ok = append_list(lines, (long long)"#define pthread_rwlock_destroy(r) ((void)(r), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_join(t, r) ((void)(t), (void)(r), 0)\n"); + ok = append_list(lines, (long long)"#define pthread_detach(t) ((void)(t), 0)\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#ifndef _WIN32\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#if defined(__APPLE__)\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#if defined(__linux__)\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on\n"); + ok = append_list(lines, (long long)" Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a\n"); + ok = append_list(lines, (long long)" /dev/urandom read elsewhere. Only if all of those are unavailable does it\n"); + ok = append_list(lines, (long long)" fall back to rand() — never on a supported platform. */\n"); + ok = append_list(lines, (long long)"static void ep_secure_random_bytes(unsigned char* buf, size_t n) {\n"); + ok = append_list(lines, (long long)"#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)\n"); + ok = append_list(lines, (long long)" arc4random_buf(buf, n);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" size_t got = 0;\n"); + ok = append_list(lines, (long long)" #if defined(__linux__)\n"); + ok = append_list(lines, (long long)" while (got < n) {\n"); + ok = append_list(lines, (long long)" ssize_t r = getrandom(buf + got, n - got, 0);\n"); + ok = append_list(lines, (long long)" if (r <= 0) break;\n"); + ok = append_list(lines, (long long)" got += (size_t)r;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" #endif\n"); + ok = append_list(lines, (long long)" if (got < n) {\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(\"/dev/urandom\", \"rb\");\n"); + ok = append_list(lines, (long long)" if (f) {\n"); + ok = append_list(lines, (long long)" got += fread(buf + got, 1, n - got, f);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" while (got < n) {\n"); + ok = append_list(lines, (long long)" buf[got++] = (unsigned char)(rand() & 0xFF);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Try/catch infrastructure */\n"); + ok = append_list(lines, (long long)"static jmp_buf ep_try_buf;\n"); + ok = append_list(lines, (long long)"static volatile int ep_try_active = 0;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_signal_handler(int sig) {\n"); + ok = append_list(lines, (long long)" if (ep_try_active) {\n"); + ok = append_list(lines, (long long)" ep_try_active = 0;\n"); + ok = append_list(lines, (long long)" longjmp(ep_try_buf, sig);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Outside try: print error and exit */\n"); + ok = append_list(lines, (long long)" const char* name = sig == SIGSEGV ? \"segmentation fault (null pointer or invalid memory access)\"\n"); + ok = append_list(lines, (long long)" : sig == SIGFPE ? \"arithmetic error (division by zero)\"\n"); + ok = append_list(lines, (long long)" : sig == SIGABRT ? \"aborted\"\n"); + ok = append_list(lines, (long long)" : \"unknown signal\";\n"); + ok = append_list(lines, (long long)" fprintf(stderr, \"\\nRuntime Error: %s (signal %d)\\n\", name, sig);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" /* Write to daemon/general log file if environment variable is set */\n"); + ok = append_list(lines, (long long)" const char* daemon_log = getenv(\"ERNOS_DAEMON_LOG\");\n"); + ok = append_list(lines, (long long)" if (!daemon_log || daemon_log[0] == '\\0') {\n"); + ok = append_list(lines, (long long)" daemon_log = getenv(\"ERNOS_LOG_FILE\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (daemon_log && daemon_log[0] != '\\0') {\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(daemon_log, \"ab\");\n"); + ok = append_list(lines, (long long)" if (f) {\n"); + ok = append_list(lines, (long long)" time_t rawtime;\n"); + ok = append_list(lines, (long long)" time(&rawtime);\n"); + ok = append_list(lines, (long long)" struct tm * timeinfo = localtime(&rawtime);\n"); + ok = append_list(lines, (long long)" char time_buf[80];\n"); + ok = append_list(lines, (long long)" if (timeinfo) {\n"); + ok = append_list(lines, (long long)" strftime(time_buf, sizeof(time_buf), \"%Y-%m-%d %H:%M:%S\", timeinfo);\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" snprintf(time_buf, sizeof(time_buf), \"%lld\", (long long)rawtime);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" fprintf(f, \"[%s] FATAL: Runtime Error: %s (signal %d)\\n\", time_buf, name, sig);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" _exit(128 + sig);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#ifdef _MSC_VER\n"); + ok = append_list(lines, (long long)"static void ep_install_signal_handlers(void);\n"); + ok = append_list(lines, (long long)"#pragma section(\".CRT$XCU\", read)\n"); + ok = append_list(lines, (long long)"__declspec(allocate(\".CRT$XCU\")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers;\n"); + ok = append_list(lines, (long long)"static void ep_install_signal_handlers(void) {\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"__attribute__((constructor))\n"); + ok = append_list(lines, (long long)"static void ep_install_signal_handlers(void) {\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" signal(SIGFPE, ep_signal_handler);\n"); + ok = append_list(lines, (long long)" signal(SIGSEGV, ep_signal_handler);\n"); + ok = append_list(lines, (long long)" signal(SIGABRT, ep_signal_handler);\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#if defined(__wasm__)\n"); + ok = append_list(lines, (long long)" typedef int ep_thread_t;\n"); + ok = append_list(lines, (long long)" typedef int ep_mutex_t;\n"); + ok = append_list(lines, (long long)" typedef int ep_cond_t;\n"); + ok = append_list(lines, (long long)" #define ep_mutex_init(m) (void)(0)\n"); + ok = append_list(lines, (long long)" #define ep_mutex_lock(m) (void)(0)\n"); + ok = append_list(lines, (long long)" #define ep_mutex_unlock(m) (void)(0)\n"); + ok = append_list(lines, (long long)" #define ep_cond_init(c) (void)(0)\n"); + ok = append_list(lines, (long long)" #define ep_cond_wait(c, m) (void)(0)\n"); + ok = append_list(lines, (long long)" #define ep_cond_signal(c) (void)(0)\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_1() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"#elif defined(_WIN32)\n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #pragma comment(lib, \"ws2_32.lib\")\n"); + ok = append_list(lines, (long long)" typedef HANDLE ep_thread_t;\n"); + ok = append_list(lines, (long long)" typedef CRITICAL_SECTION ep_mutex_t;\n"); + ok = append_list(lines, (long long)" typedef CONDITION_VARIABLE ep_cond_t;\n"); + ok = append_list(lines, (long long)" #define ep_mutex_init(m) InitializeCriticalSection(m)\n"); + ok = append_list(lines, (long long)" #define ep_mutex_lock(m) EnterCriticalSection(m)\n"); + ok = append_list(lines, (long long)" #define ep_mutex_unlock(m) LeaveCriticalSection(m)\n"); + ok = append_list(lines, (long long)" #define ep_cond_init(c) InitializeConditionVariable(c)\n"); + ok = append_list(lines, (long long)" #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)\n"); + ok = append_list(lines, (long long)" #define ep_cond_signal(c) WakeConditionVariable(c)\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" typedef pthread_t ep_thread_t;\n"); + ok = append_list(lines, (long long)" typedef pthread_mutex_t ep_mutex_t;\n"); + ok = append_list(lines, (long long)" typedef pthread_cond_t ep_cond_t;\n"); + ok = append_list(lines, (long long)" #define ep_mutex_init(m) pthread_mutex_init(m, NULL)\n"); + ok = append_list(lines, (long long)" #define ep_mutex_lock(m) pthread_mutex_lock(m)\n"); + ok = append_list(lines, (long long)" #define ep_mutex_unlock(m) pthread_mutex_unlock(m)\n"); + ok = append_list(lines, (long long)" #define ep_cond_init(c) pthread_cond_init(c, NULL)\n"); + ok = append_list(lines, (long long)" #define ep_cond_wait(c, m) pthread_cond_wait(c, m)\n"); + ok = append_list(lines, (long long)" #define ep_cond_signal(c) pthread_cond_signal(c)\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#if !defined(__wasm__) && !defined(_WIN32)\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef enum {\n"); + ok = append_list(lines, (long long)" EP_OBJ_LIST,\n"); + ok = append_list(lines, (long long)" EP_OBJ_STRING,\n"); + ok = append_list(lines, (long long)" EP_OBJ_STRUCT,\n"); + ok = append_list(lines, (long long)" EP_OBJ_CLOSURE,\n"); + ok = append_list(lines, (long long)" EP_OBJ_MAP\n"); + ok = append_list(lines, (long long)"} EpObjKind;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct EpGCObject {\n"); + ok = append_list(lines, (long long)" EpObjKind kind;\n"); + ok = append_list(lines, (long long)" int marked;\n"); + ok = append_list(lines, (long long)" void* ptr; /* actual allocation pointer */\n"); + ok = append_list(lines, (long long)" long long size; /* payload size for structs */\n"); + ok = append_list(lines, (long long)" long long num_fields; /* number of fields for structs (each is long long) */\n"); + ok = append_list(lines, (long long)" int generation; /* 0 = Nursery/young, 1 = Old */\n"); + ok = append_list(lines, (long long)" struct EpGCObject* next; /* intrusive linked list */\n"); + ok = append_list(lines, (long long)"} EpGCObject;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_now_ms(void);\n"); + ok = append_list(lines, (long long)"long long ep_sleep_ms(long long ms);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct EpTask EpTask;\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" long long chan;\n"); + ok = append_list(lines, (long long)" int completed;\n"); + ok = append_list(lines, (long long)" long long value;\n"); + ok = append_list(lines, (long long)" EpTask* waiting_task;\n"); + ok = append_list(lines, (long long)"} EpFuture;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long ep_await_future(EpFuture* fut);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"struct EpTask {\n"); + ok = append_list(lines, (long long)" long long (*step)(void*); /* pointer to step function */\n"); + ok = append_list(lines, (long long)" void* args; /* pointer to step state arguments */\n"); + ok = append_list(lines, (long long)" long long args_size_bytes; /* size of args struct for GC tracing */\n"); + ok = append_list(lines, (long long)" EpTask* next; /* run-queue link pointer */\n"); + ok = append_list(lines, (long long)" EpFuture* fut; /* future associated with this task */\n"); + ok = append_list(lines, (long long)" int state; /* coroutine execution state */\n"); + ok = append_list(lines, (long long)" int is_cancelled; /* cancellation flag for structured concurrency */\n"); + ok = append_list(lines, (long long)" struct EpTask* parent; /* parent task for structured concurrency cancellation */\n"); + ok = append_list(lines, (long long)"};\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Event Loop Scheduler Globals & Functions */\n"); + ok = append_list(lines, (long long)"static EpTask* ep_run_queue_head = NULL;\n"); + ok = append_list(lines, (long long)"static EpTask* ep_run_queue_tail = NULL;\n"); + ok = append_list(lines, (long long)"static EpTask* ep_current_task = NULL;\n"); + ok = append_list(lines, (long long)"static int ep_event_loop_fd = -1; /* epoll or kqueue fd */\n"); + ok = append_list(lines, (long long)"static int ep_active_io_sources = 0;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_task_enqueue(EpTask* task) {\n"); + ok = append_list(lines, (long long)" if (!task) return;\n"); + ok = append_list(lines, (long long)" task->next = NULL;\n"); + ok = append_list(lines, (long long)" if (ep_run_queue_tail) {\n"); + ok = append_list(lines, (long long)" ep_run_queue_tail->next = task;\n"); + ok = append_list(lines, (long long)" ep_run_queue_tail = task;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" ep_run_queue_head = ep_run_queue_tail = task;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static EpTask* ep_task_dequeue(void) {\n"); + ok = append_list(lines, (long long)" if (!ep_run_queue_head) return NULL;\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n"); + ok = append_list(lines, (long long)" ep_run_queue_head = ep_run_queue_head->next;\n"); + ok = append_list(lines, (long long)" if (!ep_run_queue_head) ep_run_queue_tail = NULL;\n"); + ok = append_list(lines, (long long)" return task;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#ifndef __wasm__\n"); + ok = append_list(lines, (long long)"#ifdef __APPLE__\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_async_loop_init(void) {\n"); + ok = append_list(lines, (long long)" if (ep_event_loop_fd != -1) return;\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)" ep_event_loop_fd = 999;\n"); + ok = append_list(lines, (long long)"#elif defined(__APPLE__)\n"); + ok = append_list(lines, (long long)" ep_event_loop_fd = kqueue();\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" ep_event_loop_fd = epoll_create1(0);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct EpTimer {\n"); + ok = append_list(lines, (long long)" long long expiry_ms;\n"); + ok = append_list(lines, (long long)" EpTask* task;\n"); + ok = append_list(lines, (long long)" struct EpTimer* next;\n"); + ok = append_list(lines, (long long)"} EpTimer;\n"); + ok = append_list(lines, (long long)"static EpTimer* ep_timers_head = NULL;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_async_register_timer(long long timeout_ms, EpTask* task) {\n"); + ok = append_list(lines, (long long)" long long expiry = ep_time_now_ms() + timeout_ms;\n"); + ok = append_list(lines, (long long)" EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer));\n"); + ok = append_list(lines, (long long)" timer->expiry_ms = expiry;\n"); + ok = append_list(lines, (long long)" timer->task = task;\n"); + ok = append_list(lines, (long long)" timer->next = NULL;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" /* Insert sorted */\n"); + ok = append_list(lines, (long long)" if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) {\n"); + ok = append_list(lines, (long long)" timer->next = ep_timers_head;\n"); + ok = append_list(lines, (long long)" ep_timers_head = timer;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" EpTimer* cur = ep_timers_head;\n"); + ok = append_list(lines, (long long)" while (cur->next && cur->next->expiry_ms <= expiry) {\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_2() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" cur = cur->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" timer->next = cur->next;\n"); + ok = append_list(lines, (long long)" cur->next = timer;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long ep_get_next_timer_timeout(void) {\n"); + ok = append_list(lines, (long long)" if (!ep_timers_head) return -1; /* block indefinitely */\n"); + ok = append_list(lines, (long long)" long long now = ep_time_now_ms();\n"); + ok = append_list(lines, (long long)" long long diff = ep_timers_head->expiry_ms - now;\n"); + ok = append_list(lines, (long long)" return diff < 0 ? 0 : diff;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_process_expired_timers(void) {\n"); + ok = append_list(lines, (long long)" long long now = ep_time_now_ms();\n"); + ok = append_list(lines, (long long)" while (ep_timers_head && ep_timers_head->expiry_ms <= now) {\n"); + ok = append_list(lines, (long long)" EpTimer* expired = ep_timers_head;\n"); + ok = append_list(lines, (long long)" ep_timers_head = ep_timers_head->next;\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(expired->task);\n"); + ok = append_list(lines, (long long)" free(expired);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_async_register_read(int fd, EpTask* task) {\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)" (void)fd;\n"); + ok = append_list(lines, (long long)" (void)task;\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" ep_async_loop_init();\n"); + ok = append_list(lines, (long long)" ep_active_io_sources++;\n"); + ok = append_list(lines, (long long)"#ifdef __APPLE__\n"); + ok = append_list(lines, (long long)" struct kevent ev;\n"); + ok = append_list(lines, (long long)" EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task);\n"); + ok = append_list(lines, (long long)" kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" struct epoll_event ev;\n"); + ok = append_list(lines, (long long)" ev.events = EPOLLIN | EPOLLONESHOT;\n"); + ok = append_list(lines, (long long)" ev.data.ptr = task;\n"); + ok = append_list(lines, (long long)" if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {\n"); + ok = append_list(lines, (long long)" epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_async_wait_step(long long timeout) {\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)" if (timeout > 0) {\n"); + ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"#ifdef __APPLE__\n"); + ok = append_list(lines, (long long)" struct kevent events[16];\n"); + ok = append_list(lines, (long long)" struct timespec ts;\n"); + ok = append_list(lines, (long long)" struct timespec* p_ts = NULL;\n"); + ok = append_list(lines, (long long)" if (timeout >= 0) {\n"); + ok = append_list(lines, (long long)" ts.tv_sec = timeout / 1000;\n"); + ok = append_list(lines, (long long)" ts.tv_nsec = (timeout % 1000) * 1000000;\n"); + ok = append_list(lines, (long long)" p_ts = &ts;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts);\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < n; i++) {\n"); + ok = append_list(lines, (long long)" EpTask* t = (EpTask*)events[i].udata;\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(t);\n"); + ok = append_list(lines, (long long)" ep_active_io_sources--;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" struct epoll_event events[16];\n"); + ok = append_list(lines, (long long)" int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout);\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < n; i++) {\n"); + ok = append_list(lines, (long long)" EpTask* t = (EpTask*)events[i].data.ptr;\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(t);\n"); + ok = append_list(lines, (long long)" ep_active_io_sources--;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" ep_process_expired_timers();\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_async_loop_run(void) {\n"); + ok = append_list(lines, (long long)" ep_async_loop_init();\n"); + ok = append_list(lines, (long long)" while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) {\n"); + ok = append_list(lines, (long long)" /* 1. Run all runnable tasks */\n"); + ok = append_list(lines, (long long)" while (ep_run_queue_head) {\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\n"); + ok = append_list(lines, (long long)" if (task->is_cancelled) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" task->fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" continue;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_current_task = task;\n"); + ok = append_list(lines, (long long)" long long res = task->step(task->args);\n"); + ok = append_list(lines, (long long)" ep_current_task = NULL;\n"); + ok = append_list(lines, (long long)" if (res != -999999) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->value = res;\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n"); + ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" /* 2. If no tasks runnable, wait for I/O / timers */\n"); + ok = append_list(lines, (long long)" if (!ep_run_queue_head) {\n"); + ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n"); + ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n"); + ok = append_list(lines, (long long)" break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n"); + ok = append_list(lines, (long long)" if (timeout > 0) {\n"); + ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_process_expired_timers();\n"); + ok = append_list(lines, (long long)" continue;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" ep_async_wait_step(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long ep_await_future(EpFuture* fut) {\n"); + ok = append_list(lines, (long long)" if (!fut) return 0;\n"); + ok = append_list(lines, (long long)" while (!fut->completed) {\n"); + ok = append_list(lines, (long long)" if (ep_run_queue_head) {\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\n"); + ok = append_list(lines, (long long)" if (task) {\n"); + ok = append_list(lines, (long long)" if (task->is_cancelled) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" task->fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" EpTask* saved_current = ep_current_task;\n"); + ok = append_list(lines, (long long)" ep_current_task = task;\n"); + ok = append_list(lines, (long long)" long long res = task->step(task->args);\n"); + ok = append_list(lines, (long long)" ep_current_task = saved_current;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_3() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" if (res != -999999) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->value = res;\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n"); + ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n"); + ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n"); + ok = append_list(lines, (long long)" fprintf(stderr, \"Deadlock detected: awaiting incomplete future with no active tasks or timers.\\n\");\n"); + ok = append_list(lines, (long long)" exit(1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n"); + ok = append_list(lines, (long long)" if (timeout > 0) {\n"); + ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_process_expired_timers();\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" ep_async_wait_step(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return fut->value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind);\n"); + ok = append_list(lines, (long long)"long long create_list(void);\n"); + ok = append_list(lines, (long long)"long long append_list(long long list_ptr, long long value);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" EpFuture* futures[128];\n"); + ok = append_list(lines, (long long)" int count;\n"); + ok = append_list(lines, (long long)" int has_error;\n"); + ok = append_list(lines, (long long)"} EpTaskGroup;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" EpFuture* fut;\n"); + ok = append_list(lines, (long long)" int timer_fired;\n"); + ok = append_list(lines, (long long)"} EpTimeoutArgs;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static EpTask* ep_find_task_by_future(EpFuture* fut) {\n"); + ok = append_list(lines, (long long)" if (!fut) return NULL;\n"); + ok = append_list(lines, (long long)" EpTask* cur = ep_run_queue_head;\n"); + ok = append_list(lines, (long long)" while (cur) {\n"); + ok = append_list(lines, (long long)" if (cur->fut == fut) return cur;\n"); + ok = append_list(lines, (long long)" cur = cur->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n"); + ok = append_list(lines, (long long)" while (timer) {\n"); + ok = append_list(lines, (long long)" if (timer->task && timer->task->fut == fut) return timer->task;\n"); + ok = append_list(lines, (long long)" timer = timer->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return NULL;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_cancel_task(EpTask* task) {\n"); + ok = append_list(lines, (long long)" if (!task) return;\n"); + ok = append_list(lines, (long long)" task->is_cancelled = 1;\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" task->fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" // Cancel children in run queue\n"); + ok = append_list(lines, (long long)" EpTask* cur = ep_run_queue_head;\n"); + ok = append_list(lines, (long long)" while (cur) {\n"); + ok = append_list(lines, (long long)" if (cur->parent == task) {\n"); + ok = append_list(lines, (long long)" ep_cancel_task(cur);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" cur = cur->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" // Cancel children in timers queue\n"); + ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n"); + ok = append_list(lines, (long long)" while (timer) {\n"); + ok = append_list(lines, (long long)" if (timer->task && timer->task->parent == task) {\n"); + ok = append_list(lines, (long long)" ep_cancel_task(timer->task);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" timer = timer->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long create_task_group(void) {\n"); + ok = append_list(lines, (long long)" EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup));\n"); + ok = append_list(lines, (long long)" tg->count = 0;\n"); + ok = append_list(lines, (long long)" tg->has_error = 0;\n"); + ok = append_list(lines, (long long)" { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; }\n"); + ok = append_list(lines, (long long)" return (long long)tg;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long add_task_group(long long group_ptr, long long fut_ptr) {\n"); + ok = append_list(lines, (long long)" EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n"); + ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)fut_ptr;\n"); + ok = append_list(lines, (long long)" if (!tg || !fut) return 0;\n"); + ok = append_list(lines, (long long)" if (tg->count < 128) {\n"); + ok = append_list(lines, (long long)" tg->futures[tg->count++] = fut;\n"); + ok = append_list(lines, (long long)" // Associate the task's parent with the current task so it's cancellation-linked\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_find_task_by_future(fut);\n"); + ok = append_list(lines, (long long)" if (task) {\n"); + ok = append_list(lines, (long long)" task->parent = ep_current_task;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long wait_task_group(long long group_ptr) {\n"); + ok = append_list(lines, (long long)" EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n"); + ok = append_list(lines, (long long)" if (!tg) return 0;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" int all_done = 0;\n"); + ok = append_list(lines, (long long)" while (!all_done) {\n"); + ok = append_list(lines, (long long)" all_done = 1;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < tg->count; i++) {\n"); + ok = append_list(lines, (long long)" EpFuture* fut = tg->futures[i];\n"); + ok = append_list(lines, (long long)" if (!fut->completed) {\n"); + ok = append_list(lines, (long long)" all_done = 0;\n"); + ok = append_list(lines, (long long)" break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" if (all_done) break;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" if (ep_run_queue_head) {\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\n"); + ok = append_list(lines, (long long)" if (task) {\n"); + ok = append_list(lines, (long long)" if (task->is_cancelled) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" task->fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" EpTask* saved_current = ep_current_task;\n"); + ok = append_list(lines, (long long)" ep_current_task = task;\n"); + ok = append_list(lines, (long long)" long long res = task->step(task->args);\n"); + ok = append_list(lines, (long long)" ep_current_task = saved_current;\n"); + ok = append_list(lines, (long long)" if (res != -999999) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->value = res;\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n"); + ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_4() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n"); + ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n"); + ok = append_list(lines, (long long)" fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n"); + ok = append_list(lines, (long long)" exit(1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n"); + ok = append_list(lines, (long long)" if (timeout > 0) {\n"); + ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_process_expired_timers();\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" ep_async_wait_step(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" // Propagate cancellation/failure inside task group\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < tg->count; i++) {\n"); + ok = append_list(lines, (long long)" EpFuture* fut = tg->futures[i];\n"); + ok = append_list(lines, (long long)" if (fut->completed && fut->value == -1) {\n"); + ok = append_list(lines, (long long)" tg->has_error = 1;\n"); + ok = append_list(lines, (long long)" for (int j = 0; j < tg->count; j++) {\n"); + ok = append_list(lines, (long long)" EpFuture* other_fut = tg->futures[j];\n"); + ok = append_list(lines, (long long)" if (!other_fut->completed) {\n"); + ok = append_list(lines, (long long)" EpTask* other_task = ep_find_task_by_future(other_fut);\n"); + ok = append_list(lines, (long long)" if (other_task) {\n"); + ok = append_list(lines, (long long)" ep_cancel_task(other_task);\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" other_fut->completed = 1;\n"); + ok = append_list(lines, (long long)" other_fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < tg->count; i++) {\n"); + ok = append_list(lines, (long long)" append_list(list, tg->futures[i]->value);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long ep_timeout_timer_step(void* r) {\n"); + ok = append_list(lines, (long long)" EpTimeoutArgs* args = (EpTimeoutArgs*)r;\n"); + ok = append_list(lines, (long long)" if (args && args->fut && !args->fut->completed) {\n"); + ok = append_list(lines, (long long)" args->timer_fired = 1;\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_find_task_by_future(args->fut);\n"); + ok = append_list(lines, (long long)" if (task) {\n"); + ok = append_list(lines, (long long)" ep_cancel_task(task);\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" args->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" args->fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long async_timeout(long long timeout_ms, long long fut_ptr) {\n"); + ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)fut_ptr;\n"); + ok = append_list(lines, (long long)" if (!fut) return -1;\n"); + ok = append_list(lines, (long long)" if (fut->completed) return fut->value;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs));\n"); + ok = append_list(lines, (long long)" args->fut = fut;\n"); + ok = append_list(lines, (long long)" args->timer_fired = 0;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask));\n"); + ok = append_list(lines, (long long)" timer_task->step = ep_timeout_timer_step;\n"); + ok = append_list(lines, (long long)" timer_task->args = args;\n"); + ok = append_list(lines, (long long)" timer_task->args_size_bytes = sizeof(EpTimeoutArgs);\n"); + ok = append_list(lines, (long long)" timer_task->fut = NULL;\n"); + ok = append_list(lines, (long long)" timer_task->state = 0;\n"); + ok = append_list(lines, (long long)" timer_task->is_cancelled = 0;\n"); + ok = append_list(lines, (long long)" timer_task->parent = NULL;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" ep_async_register_timer(timeout_ms, timer_task);\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" while (!fut->completed && !(args->timer_fired)) {\n"); + ok = append_list(lines, (long long)" if (ep_run_queue_head) {\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\n"); + ok = append_list(lines, (long long)" if (task) {\n"); + ok = append_list(lines, (long long)" if (task->is_cancelled) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" task->fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" EpTask* saved_current = ep_current_task;\n"); + ok = append_list(lines, (long long)" ep_current_task = task;\n"); + ok = append_list(lines, (long long)" long long res = task->step(task->args);\n"); + ok = append_list(lines, (long long)" ep_current_task = saved_current;\n"); + ok = append_list(lines, (long long)" if (res != -999999) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" task->fut->value = res;\n"); + ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n"); + ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(task->args);\n"); + ok = append_list(lines, (long long)" free(task);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n"); + ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n"); + ok = append_list(lines, (long long)" break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n"); + ok = append_list(lines, (long long)" if (timeout > 0) {\n"); + ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_process_expired_timers();\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" ep_async_wait_step(timeout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" return fut->value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ── Awaitable async socket-readability ─────────────────────────────────────\n"); + ok = append_list(lines, (long long)" `await async_wait_readable(fd)` suspends the calling async task until `fd` is\n"); + ok = append_list(lines, (long long)" readable, letting the event loop run other tasks (e.g. another agent waiting on\n"); + ok = append_list(lines, (long long)" its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a\n"); + ok = append_list(lines, (long long)" oneshot read-readiness task with the loop, return the future. When fd becomes\n"); + ok = append_list(lines, (long long)" readable, ep_async_wait_step re-enqueues the task; its step completes the future\n"); + ok = append_list(lines, (long long)" and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n"); + ok = append_list(lines, (long long)" on ONE thread — no OS threads, no shared-heap GC race. */\n"); + ok = append_list(lines, (long long)"typedef struct { EpFuture* fut; } EpReadReadyArgs;\n"); + ok = append_list(lines, (long long)"static long long ep_read_ready_step(void* r) {\n"); + ok = append_list(lines, (long long)" EpReadReadyArgs* args = (EpReadReadyArgs*)r;\n"); + ok = append_list(lines, (long long)" if (args && args->fut) {\n"); + ok = append_list(lines, (long long)" args->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" args->fut->value = 1;\n"); + ok = append_list(lines, (long long)" if (args->fut->waiting_task) {\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_5() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" ep_task_enqueue(args->fut->waiting_task);\n"); + ok = append_list(lines, (long long)" args->fut->waiting_task = NULL;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long async_wait_readable(long long fd) {\n"); + ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n"); + ok = append_list(lines, (long long)" fut->completed = 0;\n"); + ok = append_list(lines, (long long)" fut->value = 0;\n"); + ok = append_list(lines, (long long)" fut->waiting_task = NULL;\n"); + ok = append_list(lines, (long long)" fut->chan = 0;\n"); + ok = append_list(lines, (long long)" { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n"); + ok = append_list(lines, (long long)" EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs));\n"); + ok = append_list(lines, (long long)" args->fut = fut;\n"); + ok = append_list(lines, (long long)" EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n"); + ok = append_list(lines, (long long)" task->step = ep_read_ready_step;\n"); + ok = append_list(lines, (long long)" task->args = args;\n"); + ok = append_list(lines, (long long)" task->args_size_bytes = sizeof(EpReadReadyArgs);\n"); + ok = append_list(lines, (long long)" task->fut = NULL;\n"); + ok = append_list(lines, (long long)" task->state = 0;\n"); + ok = append_list(lines, (long long)" task->is_cancelled = 0;\n"); + ok = append_list(lines, (long long)" task->parent = ep_current_task;\n"); + ok = append_list(lines, (long long)" ep_async_register_read((int)fd, task);\n"); + ok = append_list(lines, (long long)" return (long long)fut;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" EpFuture* fut;\n"); + ok = append_list(lines, (long long)"} EpSleepTimerArgs;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long ep_sleep_timer_step(void* r) {\n"); + ok = append_list(lines, (long long)" EpSleepTimerArgs* args = (EpSleepTimerArgs*)r;\n"); + ok = append_list(lines, (long long)" if (args && args->fut) {\n"); + ok = append_list(lines, (long long)" args->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" args->fut->value = 0;\n"); + ok = append_list(lines, (long long)" if (args->fut->waiting_task) {\n"); + ok = append_list(lines, (long long)" ep_task_enqueue(args->fut->waiting_task);\n"); + ok = append_list(lines, (long long)" args->fut->waiting_task = NULL;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long sleep_ms(long long ms) {\n"); + ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n"); + ok = append_list(lines, (long long)" fut->completed = 0;\n"); + ok = append_list(lines, (long long)" fut->value = 0;\n"); + ok = append_list(lines, (long long)" fut->waiting_task = NULL;\n"); + ok = append_list(lines, (long long)" fut->chan = 0;\n"); + ok = append_list(lines, (long long)" { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs));\n"); + ok = append_list(lines, (long long)" args->fut = fut;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n"); + ok = append_list(lines, (long long)" task->step = ep_sleep_timer_step;\n"); + ok = append_list(lines, (long long)" task->args = args;\n"); + ok = append_list(lines, (long long)" task->args_size_bytes = sizeof(EpSleepTimerArgs);\n"); + ok = append_list(lines, (long long)" task->fut = NULL;\n"); + ok = append_list(lines, (long long)" task->state = 0;\n"); + ok = append_list(lines, (long long)" task->is_cancelled = 0;\n"); + ok = append_list(lines, (long long)" task->parent = ep_current_task;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" ep_async_register_timer(ms, task);\n"); + ok = append_list(lines, (long long)" return (long long)fut;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static long long cancel_task(long long fut_ptr) {\n"); + ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)fut_ptr;\n"); + ok = append_list(lines, (long long)" if (fut) {\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_find_task_by_future(fut);\n"); + ok = append_list(lines, (long long)" if (task) {\n"); + ok = append_list(lines, (long long)" ep_cancel_task(task);\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" fut->completed = 1;\n"); + ok = append_list(lines, (long long)" fut->value = -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Closure environment — captures travel with the function pointer */\n"); + ok = append_list(lines, (long long)"#define EP_CLOSURE_MAGIC 0x4550434C4FL\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" long long magic;\n"); + ok = append_list(lines, (long long)" long long fn_ptr;\n"); + ok = append_list(lines, (long long)" long long env[]; /* flexible array of captured values */\n"); + ok = append_list(lines, (long long)"} EpClosure;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* GC globals */\n"); + ok = append_list(lines, (long long)"static EpGCObject* ep_gc_head = NULL;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_count = 0;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_threshold = 4096;\n"); + ok = append_list(lines, (long long)"static int ep_gc_enabled = 1;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_nursery_count = 0;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_nursery_threshold = 512;\n"); + ok = append_list(lines, (long long)"static int ep_gc_minor_count = 0;\n"); + ok = append_list(lines, (long long)"static int ep_gc_major_count = 0;\n"); + ok = append_list(lines, (long long)"static void** ep_gc_remembered_set = NULL;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_remembered_cap = 0;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_remembered_size = 0;\n"); + ok = append_list(lines, (long long)"/* Single mutex for ALL GC and thread registry operations.\n"); + ok = append_list(lines, (long long)" Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex)\n"); + ok = append_list(lines, (long long)" which caused deadlock under concurrent channel load: thread A held gc_mutex\n"); + ok = append_list(lines, (long long)" and waited for registry_mutex, thread B held registry_mutex and waited for\n"); + ok = append_list(lines, (long long)" gc_mutex. Single lock eliminates the ordering problem. */\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)"#define __thread\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in\n"); + ok = append_list(lines, (long long)" ep_gc_stop_the_world(), waits until every *other* registered thread has parked\n"); + ok = append_list(lines, (long long)" at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs\n"); + ok = append_list(lines, (long long)" concurrently with a mutator changing its roots or an object's fields — the\n"); + ok = append_list(lines, (long long)" \"marking races with running mutators\" hazard. All three fields are touched\n"); + ok = append_list(lines, (long long)" only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at\n"); + ok = append_list(lines, (long long)" safepoints are a benign optimization: a missed set just defers parking to the\n"); + ok = append_list(lines, (long long)" next safepoint, and the collector's bounded wait covers it). */\n"); + ok = append_list(lines, (long long)"static volatile int ep_gc_stop_requested = 0;\n"); + ok = append_list(lines, (long long)"static int ep_gc_parked_count = 0;\n"); + ok = append_list(lines, (long long)"static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Function pointer for channel scanning — set after EpChannel is defined.\n"); + ok = append_list(lines, (long long)" GC mark calls this to scan values in-transit in channel buffers. */\n"); + ok = append_list(lines, (long long)"static void (*ep_gc_scan_channels_major)(void) = NULL;\n"); + ok = append_list(lines, (long long)"static void (*ep_gc_scan_channels_minor)(void) = NULL;\n"); + ok = append_list(lines, (long long)"/* Function pointers for marking top-level constant/global variables, which are\n"); + ok = append_list(lines, (long long)" GC roots that live outside any function frame. Set by __ep_init_constants. */\n"); + ok = append_list(lines, (long long)"static void (*ep_gc_mark_globals_major)(void) = NULL;\n"); + ok = append_list(lines, (long long)"static void (*ep_gc_mark_globals_minor)(void) = NULL;\n"); + ok = append_list(lines, (long long)"/* Function pointers for map value traversal — set after EpMap is defined.\n"); + ok = append_list(lines, (long long)" GC mark calls these to recursively mark values stored in maps. */\n"); + ok = append_list(lines, (long long)"static void (*ep_gc_mark_map_values)(void* ptr) = NULL;\n"); + ok = append_list(lines, (long long)"static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Thread registry for GC root scanning in multi-threaded environment */\n"); + ok = append_list(lines, (long long)"#define EP_MAX_THREADS 256\n"); + ok = append_list(lines, (long long)"static __thread void* volatile ep_thread_local_top = NULL;\n"); + ok = append_list(lines, (long long)"static __thread void* ep_thread_local_bottom = NULL;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n"); + ok = append_list(lines, (long long)"static void* ep_thread_bottoms[EP_MAX_THREADS];\n"); + ok = append_list(lines, (long long)"static volatile int ep_thread_active[EP_MAX_THREADS];\n"); + ok = append_list(lines, (long long)"static int ep_num_threads = 0;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Per-thread GC root state — heap-allocated, stable across thread lifetime.\n"); + ok = append_list(lines, (long long)" Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,\n"); + ok = append_list(lines, (long long)" ep_gc_root_sp) in the global registry. When a thread exited, the __thread\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_6() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" storage was freed, leaving dangling pointers that ep_gc_mark would\n"); + ok = append_list(lines, (long long)" dereference → segfault. Now each thread gets a heap-allocated state struct\n"); + ok = append_list(lines, (long long)" that survives thread exit and is only recycled when the slot is reused. */\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" long long* roots[4096]; /* copy of root pointers, updated under lock */\n"); + ok = append_list(lines, (long long)" volatile int sp; /* current root stack pointer */\n"); + ok = append_list(lines, (long long)"} EpThreadGCState;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS];\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */\n"); + ok = append_list(lines, (long long)"#define EP_GC_MAX_ROOTS 4096\n"); + ok = append_list(lines, (long long)"static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS];\n"); + ok = append_list(lines, (long long)"static __thread int ep_gc_root_sp = 0;\n"); + ok = append_list(lines, (long long)"static __thread int ep_thread_slot = -1;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on\n"); + ok = append_list(lines, (long long)" push and retreats on pop so that per-frame push/pop counts stay balanced.\n"); + ok = append_list(lines, (long long)" Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further\n"); + ok = append_list(lines, (long long)" roots are counted but not stored (those deep-overflow locals are simply not\n"); + ok = append_list(lines, (long long)" traced) — crucially, we never overwrite or drop an outer frame's stored\n"); + ok = append_list(lines, (long long)" roots, which the old \"silently skip the push but still pop\" path did. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_push_root(long long* root) {\n"); + ok = append_list(lines, (long long)" int idx = ep_gc_root_sp;\n"); + ok = append_list(lines, (long long)" ep_gc_root_sp++;\n"); + ok = append_list(lines, (long long)" if (idx < EP_GC_MAX_ROOTS) {\n"); + ok = append_list(lines, (long long)" ep_gc_root_stack[idx] = root;\n"); + ok = append_list(lines, (long long)" /* Update the heap-allocated state so GC mark can see it safely */\n"); + ok = append_list(lines, (long long)" if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n"); + ok = append_list(lines, (long long)" ep_thread_gc_states[ep_thread_slot]->roots[idx] = root;\n"); + ok = append_list(lines, (long long)" ep_thread_gc_states[ep_thread_slot]->sp =\n"); + ok = append_list(lines, (long long)" (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"static void ep_gc_pop_roots(long long count) {\n"); + ok = append_list(lines, (long long)" ep_gc_root_sp -= (int)count;\n"); + ok = append_list(lines, (long long)" if (ep_gc_root_sp < 0) ep_gc_root_sp = 0;\n"); + ok = append_list(lines, (long long)" /* Update the heap-allocated state (clamped to the array bound) */\n"); + ok = append_list(lines, (long long)" if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n"); + ok = append_list(lines, (long long)" ep_thread_gc_states[ep_thread_slot]->sp =\n"); + ok = append_list(lines, (long long)" (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Park the calling thread if the collector has stopped the world.\n"); + ok = append_list(lines, (long long)" MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise\n"); + ok = append_list(lines, (long long)" root set) is stable while parked, so the collector can scan it race-free. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_park_if_stopped(void) {\n"); + ok = append_list(lines, (long long)" if (!ep_gc_stop_requested) return;\n"); + ok = append_list(lines, (long long)" /* Spill registers onto the stack and publish this thread's current stack top\n"); + ok = append_list(lines, (long long)" so the collector can conservatively scan its frozen C stack while parked —\n"); + ok = append_list(lines, (long long)" this catches roots held only in registers/temporaries that the precise\n"); + ok = append_list(lines, (long long)" shadow stack does not yet record. _dummy is declared below _pregs, so its\n"); + ok = append_list(lines, (long long)" (lower) address bounds a scan range that covers the spilled registers. */\n"); + ok = append_list(lines, (long long)" jmp_buf _pregs;\n"); + ok = append_list(lines, (long long)" volatile char _top_marker; /* function-scope: stays valid while parked */\n"); + ok = append_list(lines, (long long)" memset(&_pregs, 0, sizeof(_pregs));\n"); + ok = append_list(lines, (long long)" setjmp(_pregs);\n"); + ok = append_list(lines, (long long)" /* _top_marker is declared after _pregs, so its (lower) address bounds a scan\n"); + ok = append_list(lines, (long long)" range [&_top_marker, stack_bottom] that covers the spilled registers. */\n"); + ok = append_list(lines, (long long)" ep_thread_local_top = (void*)&_top_marker;\n"); + ok = append_list(lines, (long long)" __sync_synchronize(); /* publish shadow-stack + top writes before parking */\n"); + ok = append_list(lines, (long long)" ep_gc_parked_count++;\n"); + ok = append_list(lines, (long long)" while (ep_gc_stop_requested) {\n"); + ok = append_list(lines, (long long)" pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_parked_count--;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held.\n"); + ok = append_list(lines, (long long)" Waits (briefly releasing the lock so blocked mutators can reach a safepoint)\n"); + ok = append_list(lines, (long long)" until all other registered threads have parked. After a bounded fallback\n"); + ok = append_list(lines, (long long)" (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked\n"); + ok = append_list(lines, (long long)" or idle with a stable shadow stack, so scanning it is still safe in practice. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_stop_the_world(void) {\n"); + ok = append_list(lines, (long long)" ep_gc_stop_requested = 1;\n"); + ok = append_list(lines, (long long)" /* Actively-running threads reach a safepoint (every allocation and every\n"); + ok = append_list(lines, (long long)" function entry) within microseconds, so they park on the first spin or\n"); + ok = append_list(lines, (long long)" two. The bound only caps the rare case where a thread is blocked/idle\n"); + ok = append_list(lines, (long long)" (e.g. just entered a channel op) and won't park — those have a stable\n"); + ok = append_list(lines, (long long)" shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */\n"); + ok = append_list(lines, (long long)" for (int spins = 0; spins < 40; spins++) {\n"); + ok = append_list(lines, (long long)" int others = 0;\n"); + ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n"); + ok = append_list(lines, (long long)" if (ep_thread_active[t] && t != ep_thread_slot) others++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (others <= 0 || ep_gc_parked_count >= others) return;\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" Sleep(1);\n"); + ok = append_list(lines, (long long)"#elif !defined(__wasm__)\n"); + ok = append_list(lines, (long long)" usleep(250);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_start_the_world(void) {\n"); + ok = append_list(lines, (long long)" ep_gc_stop_requested = 0;\n"); + ok = append_list(lines, (long long)" pthread_cond_broadcast(&ep_gc_resume_cond);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_register_thread(void* stack_bottom) {\n"); + ok = append_list(lines, (long long)" ep_thread_local_bottom = stack_bottom;\n"); + ok = append_list(lines, (long long)" ep_thread_local_top = stack_bottom;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" int slot = -1;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < ep_num_threads; i++) {\n"); + ok = append_list(lines, (long long)" if (!ep_thread_active[i]) {\n"); + ok = append_list(lines, (long long)" slot = i;\n"); + ok = append_list(lines, (long long)" break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {\n"); + ok = append_list(lines, (long long)" slot = ep_num_threads++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (slot != -1) {\n"); + ok = append_list(lines, (long long)" ep_thread_tops[slot] = &ep_thread_local_top;\n"); + ok = append_list(lines, (long long)" ep_thread_bottoms[slot] = stack_bottom;\n"); + ok = append_list(lines, (long long)" /* Allocate or reuse heap state for this slot */\n"); + ok = append_list(lines, (long long)" if (!ep_thread_gc_states[slot]) {\n"); + ok = append_list(lines, (long long)" ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState));\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_thread_gc_states[slot]->sp = 0;\n"); + ok = append_list(lines, (long long)" ep_thread_slot = slot;\n"); + ok = append_list(lines, (long long)" __sync_synchronize(); /* Memory barrier: state must be visible before active */\n"); + ok = append_list(lines, (long long)" ep_thread_active[slot] = 1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_unregister_thread(void) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < ep_num_threads; i++) {\n"); + ok = append_list(lines, (long long)" if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n"); + ok = append_list(lines, (long long)" /* Zero root count FIRST — even if ep_gc_mark races past the\n"); + ok = append_list(lines, (long long)" active check, it will see sp=0 and walk no roots instead\n"); + ok = append_list(lines, (long long)" of dereferencing stale __thread pointers */\n"); + ok = append_list(lines, (long long)" if (ep_thread_gc_states[i]) {\n"); + ok = append_list(lines, (long long)" ep_thread_gc_states[i]->sp = 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */\n"); + ok = append_list(lines, (long long)" ep_thread_active[i] = 0;\n"); + ok = append_list(lines, (long long)" ep_thread_slot = -1;\n"); + ok = append_list(lines, (long long)" break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_7() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" void* key;\n"); + ok = append_list(lines, (long long)" EpGCObject* value;\n"); + ok = append_list(lines, (long long)"} EpGCEntry;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static EpGCEntry* ep_gc_table = NULL;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_table_cap = 0;\n"); + ok = append_list(lines, (long long)"static long long ep_gc_table_size = 0;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap)\n"); + ok = append_list(lines, (long long)" with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4\n"); + ok = append_list(lines, (long long)" bits are always 0 and only every 16th bucket was ever a home slot. That caused\n"); + ok = append_list(lines, (long long)" catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's\n"); + ok = append_list(lines, (long long)" rehash became O(n^2), which (under the single global GC mutex) wedged the whole\n"); + ok = append_list(lines, (long long)" node when a large object list was freed. A splitmix64 finalizer avalanches all\n"); + ok = append_list(lines, (long long)" bits, so even the low bits taken by the (cap-1) mask are well distributed. */\n"); + ok = append_list(lines, (long long)"static inline long long ep_gc_index(void* key, long long cap) {\n"); + ok = append_list(lines, (long long)" uint64_t z = (uint64_t)(uintptr_t)key;\n"); + ok = append_list(lines, (long long)" z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;\n"); + ok = append_list(lines, (long long)" z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;\n"); + ok = append_list(lines, (long long)" z = z ^ (z >> 31);\n"); + ok = append_list(lines, (long long)" return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Insert without growing — assumes a free slot exists. Used by the resize and by\n"); + ok = append_list(lines, (long long)" ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of\n"); + ok = append_list(lines, (long long)" the table mid-iteration. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_table_place(void* key, EpGCObject* value) {\n"); + ok = append_list(lines, (long long)" long long idx = ep_gc_index(key, ep_gc_table_cap);\n"); + ok = append_list(lines, (long long)" while (ep_gc_table[idx].key != NULL) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_table[idx].key == key) {\n"); + ok = append_list(lines, (long long)" ep_gc_table[idx].value = value;\n"); + ok = append_list(lines, (long long)" return;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" idx = (idx + 1) & (ep_gc_table_cap - 1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_table[idx].key = key;\n"); + ok = append_list(lines, (long long)" ep_gc_table[idx].value = value;\n"); + ok = append_list(lines, (long long)" ep_gc_table_size++;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_table_insert(void* key, EpGCObject* value) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_table_size * 2 >= ep_gc_table_cap) {\n"); + ok = append_list(lines, (long long)" long long old_cap = ep_gc_table_cap;\n"); + ok = append_list(lines, (long long)" long long new_cap = old_cap == 0 ? 512 : old_cap * 2;\n"); + ok = append_list(lines, (long long)" EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));\n"); + ok = append_list(lines, (long long)" EpGCEntry* old_table = ep_gc_table;\n"); + ok = append_list(lines, (long long)" ep_gc_table = new_table;\n"); + ok = append_list(lines, (long long)" ep_gc_table_cap = new_cap;\n"); + ok = append_list(lines, (long long)" ep_gc_table_size = 0;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < old_cap; i++) {\n"); + ok = append_list(lines, (long long)" if (old_table[i].key != NULL) {\n"); + ok = append_list(lines, (long long)" ep_gc_table_place(old_table[i].key, old_table[i].value);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(old_table);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_table_place(key, value);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static EpGCObject* ep_gc_table_get(void* key) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_table_cap == 0) return NULL;\n"); + ok = append_list(lines, (long long)" long long idx = ep_gc_index(key, ep_gc_table_cap);\n"); + ok = append_list(lines, (long long)" while (ep_gc_table[idx].key != NULL) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;\n"); + ok = append_list(lines, (long long)" idx = (idx + 1) & (ep_gc_table_cap - 1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return NULL;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_table_remove(void* key) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_table_cap == 0) return;\n"); + ok = append_list(lines, (long long)" long long idx = ep_gc_index(key, ep_gc_table_cap);\n"); + ok = append_list(lines, (long long)" while (ep_gc_table[idx].key != NULL) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_table[idx].key == key) {\n"); + ok = append_list(lines, (long long)" ep_gc_table[idx].key = NULL;\n"); + ok = append_list(lines, (long long)" ep_gc_table[idx].value = NULL;\n"); + ok = append_list(lines, (long long)" ep_gc_table_size--;\n"); + ok = append_list(lines, (long long)" /* Backward-shift rehash of the rest of this cluster. Re-place (no\n"); + ok = append_list(lines, (long long)" resize: size is not growing) so a mid-iteration realloc can never\n"); + ok = append_list(lines, (long long)" free the table out from under this loop. */\n"); + ok = append_list(lines, (long long)" long long next_idx = (idx + 1) & (ep_gc_table_cap - 1);\n"); + ok = append_list(lines, (long long)" while (ep_gc_table[next_idx].key != NULL) {\n"); + ok = append_list(lines, (long long)" void* rehash_key = ep_gc_table[next_idx].key;\n"); + ok = append_list(lines, (long long)" EpGCObject* rehash_val = ep_gc_table[next_idx].value;\n"); + ok = append_list(lines, (long long)" ep_gc_table[next_idx].key = NULL;\n"); + ok = append_list(lines, (long long)" ep_gc_table[next_idx].value = NULL;\n"); + ok = append_list(lines, (long long)" ep_gc_table_size--;\n"); + ok = append_list(lines, (long long)" ep_gc_table_place(rehash_key, rehash_val);\n"); + ok = append_list(lines, (long long)" next_idx = (next_idx + 1) & (ep_gc_table_cap - 1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" idx = (idx + 1) & (ep_gc_table_cap - 1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Register a new GC object */\n"); + ok = append_list(lines, (long long)"static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {\n"); + ok = append_list(lines, (long long)" if (!ptr) return NULL;\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */\n"); + ok = append_list(lines, (long long)" EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));\n"); + ok = append_list(lines, (long long)" if (!obj) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" return NULL;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" obj->kind = kind;\n"); + ok = append_list(lines, (long long)" obj->marked = 0;\n"); + ok = append_list(lines, (long long)" obj->ptr = ptr;\n"); + ok = append_list(lines, (long long)" obj->size = 0;\n"); + ok = append_list(lines, (long long)" obj->num_fields = 0;\n"); + ok = append_list(lines, (long long)" obj->generation = 0;\n"); + ok = append_list(lines, (long long)" obj->next = ep_gc_head;\n"); + ok = append_list(lines, (long long)" ep_gc_head = obj;\n"); + ok = append_list(lines, (long long)" ep_gc_count++;\n"); + ok = append_list(lines, (long long)" ep_gc_nursery_count++;\n"); + ok = append_list(lines, (long long)" ep_gc_table_insert(ptr, obj);\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" return obj;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Find GC object by pointer.\n"); + ok = append_list(lines, (long long)" Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table\n"); + ok = append_list(lines, (long long)" concurrently (from another thread's allocation). Mutator-side callers\n"); + ok = append_list(lines, (long long)" (write barrier, free_struct/free_map/free_list, to-string) must use this\n"); + ok = append_list(lines, (long long)" locking variant; code already holding the mutex (mark/sweep) calls\n"); + ok = append_list(lines, (long long)" ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */\n"); + ok = append_list(lines, (long long)"static EpGCObject* ep_gc_find(void* ptr) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint */\n"); + ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" return obj;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).\n"); + ok = append_list(lines, (long long)" The whole operation runs under ep_gc_mutex so the table lookups and the\n"); + ok = append_list(lines, (long long)" remembered-set update see a consistent table (no race with a concurrent\n"); + ok = append_list(lines, (long long)" resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_write_barrier(void* host_ptr, long long val) {\n"); + ok = append_list(lines, (long long)" if (val == 0) return;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_8() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */\n"); + ok = append_list(lines, (long long)" EpGCObject* host_obj = ep_gc_table_get(host_ptr);\n"); + ok = append_list(lines, (long long)" EpGCObject* val_obj = ep_gc_table_get((void*)val);\n"); + ok = append_list(lines, (long long)" if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) {\n"); + ok = append_list(lines, (long long)" /* Check if already in remembered set */\n"); + ok = append_list(lines, (long long)" int found = 0;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < ep_gc_remembered_size; i++) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_remembered_set[i] == (void*)val) {\n"); + ok = append_list(lines, (long long)" found = 1;\n"); + ok = append_list(lines, (long long)" break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (!found) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_remembered_size >= ep_gc_remembered_cap) {\n"); + ok = append_list(lines, (long long)" long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2;\n"); + ok = append_list(lines, (long long)" void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*));\n"); + ok = append_list(lines, (long long)" if (new_set) {\n"); + ok = append_list(lines, (long long)" ep_gc_remembered_set = new_set;\n"); + ok = append_list(lines, (long long)" ep_gc_remembered_cap = new_cap;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (ep_gc_remembered_size < ep_gc_remembered_cap) {\n"); + ok = append_list(lines, (long long)" ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Forward declarations for list type (needed by GC mark) */\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" long long* data;\n"); + ok = append_list(lines, (long long)" long long length;\n"); + ok = append_list(lines, (long long)" long long capacity;\n"); + ok = append_list(lines, (long long)"} EpList;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* A real heap object (list/map/string) is malloc'd, so its address is far above\n"); + ok = append_list(lines, (long long)" the never-mapped first page. EP values that are NOT pointers — small ints,\n"); + ok = append_list(lines, (long long)" booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096).\n"); + ok = append_list(lines, (long long)" Guarding the object accessors with this turns \"deref a non-pointer\" (the cause\n"); + ok = append_list(lines, (long long)" of the read_transcripts segfault, and that whole class) into a safe null return\n"); + ok = append_list(lines, (long long)" instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */\n"); + ok = append_list(lines, (long long)"#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL)\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Mark a single object and recursively mark its children */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_object(void* ptr) {\n"); + ok = append_list(lines, (long long)" if (!ptr) return;\n"); + ok = append_list(lines, (long long)" /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n"); + ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n"); + ok = append_list(lines, (long long)" if (!obj || obj->marked) return;\n"); + ok = append_list(lines, (long long)" obj->marked = 1;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" if (obj->kind == EP_OBJ_LIST) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)ptr;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n"); + ok = append_list(lines, (long long)" long long val = list->data[i];\n"); + ok = append_list(lines, (long long)" if (val != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_STRUCT) {\n"); + ok = append_list(lines, (long long)" long long* fields = (long long*)ptr;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < obj->num_fields; i++) {\n"); + ok = append_list(lines, (long long)" if (fields[i] != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)fields[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_MAP) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Mark a single object and recursively mark its children (only if it is Gen 0) */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_object_minor(void* ptr) {\n"); + ok = append_list(lines, (long long)" if (!ptr) return;\n"); + ok = append_list(lines, (long long)" /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n"); + ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n"); + ok = append_list(lines, (long long)" if (!obj || obj->generation != 0 || obj->marked) return;\n"); + ok = append_list(lines, (long long)" obj->marked = 1;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" if (obj->kind == EP_OBJ_LIST) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)ptr;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n"); + ok = append_list(lines, (long long)" long long val = list->data[i];\n"); + ok = append_list(lines, (long long)" if (val != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_STRUCT) {\n"); + ok = append_list(lines, (long long)" long long* fields = (long long*)ptr;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < obj->num_fields; i++) {\n"); + ok = append_list(lines, (long long)" if (fields[i] != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)fields[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_MAP) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Conservatively scan every registered thread's C stack and mark any word that\n"); + ok = append_list(lines, (long long)" looks like a tracked pointer. The collector spills its own registers and\n"); + ok = append_list(lines, (long long)" publishes its top here; all other threads are parked at a safepoint with their\n"); + ok = append_list(lines, (long long)" registers spilled and top published (ep_gc_park_if_stopped), so their stacks\n"); + ok = append_list(lines, (long long)" are frozen. This complements the precise shadow stacks: it catches roots held\n"); + ok = append_list(lines, (long long)" only in registers/temporaries (e.g. a freshly allocated object not yet stored\n"); + ok = append_list(lines, (long long)" into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find.\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" Only run on MAJOR collections: minor collections rely on the precise shadow\n"); + ok = append_list(lines, (long long)" stacks plus the write barrier's remembered set (the standard generational\n"); + ok = append_list(lines, (long long)" approach), so they do no stack scan at all — which means there is no racy\n"); + ok = append_list(lines, (long long)" cross-thread stack read on the frequent minor path either. The expensive\n"); + ok = append_list(lines, (long long)" full-stack scan is paid only on the rarer major collection, where it pins\n"); + ok = append_list(lines, (long long)" any long-lived object reachable only via a register across many GCs.\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" Marked no_sanitize_address: a conservative scan deliberately reads whole stack\n"); + ok = append_list(lines, (long long)" ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */\n"); + ok = append_list(lines, (long long)"#if defined(__SANITIZE_ADDRESS__)\n"); + ok = append_list(lines, (long long)"# define EP_NO_ASAN __attribute__((no_sanitize_address))\n"); + ok = append_list(lines, (long long)"#elif defined(__has_feature)\n"); + ok = append_list(lines, (long long)"# if __has_feature(address_sanitizer)\n"); + ok = append_list(lines, (long long)"# define EP_NO_ASAN __attribute__((no_sanitize_address))\n"); + ok = append_list(lines, (long long)"# endif\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"#ifndef EP_NO_ASAN\n"); + ok = append_list(lines, (long long)"# define EP_NO_ASAN\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"EP_NO_ASAN\n"); + ok = append_list(lines, (long long)"static void ep_gc_scan_thread_stacks(void) {\n"); + ok = append_list(lines, (long long)" jmp_buf _regs;\n"); + ok = append_list(lines, (long long)" volatile char _top_marker;\n"); + ok = append_list(lines, (long long)" memset(&_regs, 0, sizeof(_regs));\n"); + ok = append_list(lines, (long long)" setjmp(_regs); /* spill the collector's own registers onto its stack */\n"); + ok = append_list(lines, (long long)" /* Publish the LOWEST of our own local addresses as this thread's live top, so the\n"); + ok = append_list(lines, (long long)" scanned range covers both the stack marker and the register-spill buffer whatever\n"); + ok = append_list(lines, (long long)" order the compiler laid them out (a missed _regs would drop a register-only root). */\n"); + ok = append_list(lines, (long long)" { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;\n"); + ok = append_list(lines, (long long)" ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }\n"); + ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n"); + ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n"); + ok = append_list(lines, (long long)" if (!ep_thread_tops[t]) continue;\n"); + ok = append_list(lines, (long long)" /* The published top comes from a char local, so it may not be pointer-aligned;\n"); + ok = append_list(lines, (long long)" mask DOWN to 8 bytes. Aligning down only widens the conservative window by a\n"); + ok = append_list(lines, (long long)" few harmless bytes — aligning up could skip the slot holding a live root.\n"); + ok = append_list(lines, (long long)" Unaligned void** dereferences are UB and produce a skewed scan window on\n"); + ok = append_list(lines, (long long)" strict platforms (caught by valgrind on Linux). */\n"); + ok = append_list(lines, (long long)" void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);\n"); + ok = append_list(lines, (long long)" void** end = (void**)ep_thread_bottoms[t];\n"); + ok = append_list(lines, (long long)" if (!start || !end) continue;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_9() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" if (start > end) { void** tmp = start; start = end; end = tmp; }\n"); + ok = append_list(lines, (long long)" for (void** cur = start; cur < end; cur++) {\n"); + ok = append_list(lines, (long long)" void* p = *cur;\n"); + ok = append_list(lines, (long long)" if (p) ep_gc_mark_object(p);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Mark phase: traverse from ALL threads' explicit GC roots.\n"); + ok = append_list(lines, (long long)" Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark(void) {\n"); + ok = append_list(lines, (long long)" ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */\n"); + ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n"); + ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n"); + ok = append_list(lines, (long long)" EpThreadGCState* state = ep_thread_gc_states[t];\n"); + ok = append_list(lines, (long long)" if (!state) continue;\n"); + ok = append_list(lines, (long long)" int sp = state->sp;\n"); + ok = append_list(lines, (long long)" if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < sp; i++) {\n"); + ok = append_list(lines, (long long)" long long* root_ptr = state->roots[i];\n"); + ok = append_list(lines, (long long)" if (!root_ptr) continue;\n"); + ok = append_list(lines, (long long)" long long val = *root_ptr;\n"); + ok = append_list(lines, (long long)" if (val != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Also mark from main thread's local root stack (thread 0 / unregistered) */\n"); + ok = append_list(lines, (long long)" int local_sp = ep_gc_root_sp;\n"); + ok = append_list(lines, (long long)" if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < local_sp; i++) {\n"); + ok = append_list(lines, (long long)" long long val = *ep_gc_root_stack[i];\n"); + ok = append_list(lines, (long long)" if (val != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Mark active tasks in the scheduler run queue */\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n"); + ok = append_list(lines, (long long)" while (task) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)task->fut);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (task->args && task->args_size_bytes > 0) {\n"); + ok = append_list(lines, (long long)" long long* ptr = (long long*)task->args;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < task->args_size_bytes / 8; i++) {\n"); + ok = append_list(lines, (long long)" long long val = ptr[i];\n"); + ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" task = task->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Mark active tasks in the timers queue */\n"); + ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n"); + ok = append_list(lines, (long long)" while (timer) {\n"); + ok = append_list(lines, (long long)" if (timer->task) {\n"); + ok = append_list(lines, (long long)" EpTask* t = timer->task;\n"); + ok = append_list(lines, (long long)" if (t->fut) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)t->fut);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (t->args && t->args_size_bytes > 0) {\n"); + ok = append_list(lines, (long long)" long long* ptr = (long long*)t->args;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < t->args_size_bytes / 8; i++) {\n"); + ok = append_list(lines, (long long)" long long val = ptr[i];\n"); + ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" timer = timer->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Mark top-level constant/global variables (roots outside any frame) */\n"); + ok = append_list(lines, (long long)" if (ep_gc_mark_globals_major) ep_gc_mark_globals_major();\n"); + ok = append_list(lines, (long long)" /* Scan all registered channel buffers — values in-transit have no root */\n"); + ok = append_list(lines, (long long)" if (ep_gc_scan_channels_major) ep_gc_scan_channels_major();\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it\n"); + ok = append_list(lines, (long long)" finds. This closes a use-after-free on the frequent minor path: a freshly-allocated\n"); + ok = append_list(lines, (long long)" argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating\n"); + ok = append_list(lines, (long long)" h() — lives only on the C stack / in registers and is not yet on the precise shadow\n"); + ok = append_list(lines, (long long)" stack, so a minor collection triggered mid-expression would otherwise free it. Scanning\n"); + ok = append_list(lines, (long long)" ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap\n"); + ok = append_list(lines, (long long)" (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by\n"); + ok = append_list(lines, (long long)" ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held\n"); + ok = append_list(lines, (long long)" roots onto the stack so the scan can see them. */\n"); + ok = append_list(lines, (long long)"EP_NO_ASAN\n"); + ok = append_list(lines, (long long)"static void ep_gc_scan_own_stack_minor(void) {\n"); + ok = append_list(lines, (long long)" jmp_buf _regs;\n"); + ok = append_list(lines, (long long)" volatile char _marker;\n"); + ok = append_list(lines, (long long)" memset(&_regs, 0, sizeof(_regs));\n"); + ok = append_list(lines, (long long)" setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */\n"); + ok = append_list(lines, (long long)" void* bottom = ep_thread_local_bottom;\n"); + ok = append_list(lines, (long long)" if (!bottom) return;\n"); + ok = append_list(lines, (long long)" /* Start at the LOWEST of our own local addresses so the scanned range covers both\n"); + ok = append_list(lines, (long long)" the current stack top (_marker) and the register-spill buffer (_regs), regardless\n"); + ok = append_list(lines, (long long)" of how the compiler ordered these locals on the stack. Missing _regs would drop a\n"); + ok = append_list(lines, (long long)" root held only in a callee-saved register -> a rare use-after-free. */\n"); + ok = append_list(lines, (long long)" char* a = (char*)(void*)&_marker;\n"); + ok = append_list(lines, (long long)" char* b = (char*)(void*)&_regs;\n"); + ok = append_list(lines, (long long)" char* lo = (a < b) ? a : b;\n"); + ok = append_list(lines, (long long)" /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8\n"); + ok = append_list(lines, (long long)" bytes. Aligning down only widens the conservative window by a few harmless\n"); + ok = append_list(lines, (long long)" bytes — aligning up could skip the slot holding a live root. Unaligned void**\n"); + ok = append_list(lines, (long long)" dereferences are UB and skew the scan window on strict platforms (valgrind). */\n"); + ok = append_list(lines, (long long)" void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7);\n"); + ok = append_list(lines, (long long)" void** end = (void**)bottom;\n"); + ok = append_list(lines, (long long)" if (start > end) { void** tmp = start; start = end; end = tmp; }\n"); + ok = append_list(lines, (long long)" for (void** cur = start; cur < end; cur++) {\n"); + ok = append_list(lines, (long long)" void* p = *cur;\n"); + ok = append_list(lines, (long long)" if (p) ep_gc_mark_object_minor(p);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_minor(void) {\n"); + ok = append_list(lines, (long long)" /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument\n"); + ok = append_list(lines, (long long)" temporaries (only on the stack / in registers, not yet on the shadow stack) that a\n"); + ok = append_list(lines, (long long)" minor collection mid-expression would otherwise free. Own-thread only, so race-free. */\n"); + ok = append_list(lines, (long long)" ep_gc_scan_own_stack_minor();\n"); + ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n"); + ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n"); + ok = append_list(lines, (long long)" EpThreadGCState* state = ep_thread_gc_states[t];\n"); + ok = append_list(lines, (long long)" if (!state) continue;\n"); + ok = append_list(lines, (long long)" int sp = state->sp;\n"); + ok = append_list(lines, (long long)" if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < sp; i++) {\n"); + ok = append_list(lines, (long long)" long long* root_ptr = state->roots[i];\n"); + ok = append_list(lines, (long long)" if (!root_ptr) continue;\n"); + ok = append_list(lines, (long long)" long long val = *root_ptr;\n"); + ok = append_list(lines, (long long)" if (val != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" int local_sp = ep_gc_root_sp;\n"); + ok = append_list(lines, (long long)" if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < local_sp; i++) {\n"); + ok = append_list(lines, (long long)" long long val = *ep_gc_root_stack[i];\n"); + ok = append_list(lines, (long long)" if (val != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Mark active tasks in the scheduler run queue for minor collection */\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n"); + ok = append_list(lines, (long long)" while (task) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)task->fut);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (task->args && task->args_size_bytes > 0) {\n"); + ok = append_list(lines, (long long)" long long* ptr = (long long*)task->args;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < task->args_size_bytes / 8; i++) {\n"); + ok = append_list(lines, (long long)" long long val = ptr[i];\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_10() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object_minor((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" task = task->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Mark active tasks in the timers queue for minor collection */\n"); + ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n"); + ok = append_list(lines, (long long)" while (timer) {\n"); + ok = append_list(lines, (long long)" if (timer->task) {\n"); + ok = append_list(lines, (long long)" EpTask* t = timer->task;\n"); + ok = append_list(lines, (long long)" if (t->fut) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)t->fut);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (t->args && t->args_size_bytes > 0) {\n"); + ok = append_list(lines, (long long)" long long* ptr = (long long*)t->args;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < t->args_size_bytes / 8; i++) {\n"); + ok = append_list(lines, (long long)" long long val = ptr[i];\n"); + ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object_minor((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" timer = timer->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Also mark from the remembered set */\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < ep_gc_remembered_size; i++) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor(ep_gc_remembered_set[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Mark top-level constant/global variables (roots outside any frame) */\n"); + ok = append_list(lines, (long long)" if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor();\n"); + ok = append_list(lines, (long long)" /* Scan all registered channel buffers — values in-transit have no root */\n"); + ok = append_list(lines, (long long)" if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor();\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_sweep_minor(void) {\n"); + ok = append_list(lines, (long long)" EpGCObject** cur = &ep_gc_head;\n"); + ok = append_list(lines, (long long)" while (*cur) {\n"); + ok = append_list(lines, (long long)" if ((*cur)->generation == 0) {\n"); + ok = append_list(lines, (long long)" if (!(*cur)->marked) {\n"); + ok = append_list(lines, (long long)" EpGCObject* garbage = *cur;\n"); + ok = append_list(lines, (long long)" *cur = garbage->next;\n"); + ok = append_list(lines, (long long)" ep_gc_table_remove(garbage->ptr);\n"); + ok = append_list(lines, (long long)" if (garbage->kind == EP_OBJ_LIST) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)garbage->ptr;\n"); + ok = append_list(lines, (long long)" if (list) {\n"); + ok = append_list(lines, (long long)" free(list->data);\n"); + ok = append_list(lines, (long long)" free(list);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRING) {\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRUCT) {\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_CLOSURE) {\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_MAP) {\n"); + ok = append_list(lines, (long long)" /* EpMap layout: entries*, capacity, size. Free entries then map. */\n"); + ok = append_list(lines, (long long)" void** map_fields = (void**)garbage->ptr;\n"); + ok = append_list(lines, (long long)" if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(garbage);\n"); + ok = append_list(lines, (long long)" ep_gc_count--;\n"); + ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" (*cur)->marked = 0;\n"); + ok = append_list(lines, (long long)" (*cur)->generation = 1;\n"); + ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n"); + ok = append_list(lines, (long long)" cur = &(*cur)->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" cur = &(*cur)->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_remembered_size = 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_sweep_major(void) {\n"); + ok = append_list(lines, (long long)" EpGCObject** cur = &ep_gc_head;\n"); + ok = append_list(lines, (long long)" while (*cur) {\n"); + ok = append_list(lines, (long long)" if (!(*cur)->marked) {\n"); + ok = append_list(lines, (long long)" EpGCObject* garbage = *cur;\n"); + ok = append_list(lines, (long long)" *cur = garbage->next;\n"); + ok = append_list(lines, (long long)" ep_gc_table_remove(garbage->ptr);\n"); + ok = append_list(lines, (long long)" if (garbage->generation == 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (garbage->kind == EP_OBJ_LIST) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)garbage->ptr;\n"); + ok = append_list(lines, (long long)" if (list) {\n"); + ok = append_list(lines, (long long)" free(list->data);\n"); + ok = append_list(lines, (long long)" free(list);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRING) {\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRUCT) {\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_CLOSURE) {\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_MAP) {\n"); + ok = append_list(lines, (long long)" void** map_fields = (void**)garbage->ptr;\n"); + ok = append_list(lines, (long long)" if (map_fields && map_fields[0]) free(map_fields[0]);\n"); + ok = append_list(lines, (long long)" free(garbage->ptr);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(garbage);\n"); + ok = append_list(lines, (long long)" ep_gc_count--;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" (*cur)->marked = 0;\n"); + ok = append_list(lines, (long long)" if ((*cur)->generation == 0) {\n"); + ok = append_list(lines, (long long)" (*cur)->generation = 1;\n"); + ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" cur = &(*cur)->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_remembered_size = 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_collect_minor(void) {\n"); + ok = append_list(lines, (long long)" if (!ep_gc_enabled) return;\n"); + ok = append_list(lines, (long long)" ep_gc_minor_count++;\n"); + ok = append_list(lines, (long long)" ep_gc_mark_minor();\n"); + ok = append_list(lines, (long long)" ep_gc_sweep_minor();\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_collect_major(void) {\n"); + ok = append_list(lines, (long long)" if (!ep_gc_enabled) return;\n"); + ok = append_list(lines, (long long)" ep_gc_major_count++;\n"); + ok = append_list(lines, (long long)" ep_gc_mark();\n"); + ok = append_list(lines, (long long)" ep_gc_sweep_major();\n"); + ok = append_list(lines, (long long)" ep_gc_threshold = ep_gc_count * 2;\n"); + ok = append_list(lines, (long long)" if (ep_gc_threshold < 4096) ep_gc_threshold = 4096;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Run a full GC collection — caller MUST hold ep_gc_mutex */\n"); + ok = append_list(lines, (long long)"static void ep_gc_collect(void) {\n"); + ok = append_list(lines, (long long)" ep_gc_collect_major();\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function\n"); + ok = append_list(lines, (long long)" GC safepoint: if another thread has stopped the world, park here until it's done. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_maybe_collect(void) {\n"); + ok = append_list(lines, (long long)" if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n"); + ok = append_list(lines, (long long)" /* Safepoint: lock-free fast check, then park under the lock if a collection\n"); + ok = append_list(lines, (long long)" is in progress on another thread. Keeps the no-GC path lock-free. */\n"); + ok = append_list(lines, (long long)" if (ep_gc_stop_requested) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_park_if_stopped();\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Fast path: check thresholds before acquiring mutex.\n"); + ok = append_list(lines, (long long)" Counters are only incremented under the mutex, so worst case\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_11() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" we miss one collection cycle — safe trade-off for avoiding\n"); + ok = append_list(lines, (long long)" a mutex lock/unlock (~20-50ns) on every function call. */\n"); + ok = append_list(lines, (long long)" if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;\n"); + ok = append_list(lines, (long long)" EP_GC_UPDATE_TOP();\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" /* Another thread may have started collecting between the check and the lock —\n"); + ok = append_list(lines, (long long)" park instead of racing it, then re-check thresholds under the lock. */\n"); + ok = append_list(lines, (long long)" ep_gc_park_if_stopped();\n"); + ok = append_list(lines, (long long)" if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) {\n"); + ok = append_list(lines, (long long)" ep_gc_stop_the_world();\n"); + ok = append_list(lines, (long long)" if (ep_gc_nursery_count >= ep_gc_nursery_threshold) {\n"); + ok = append_list(lines, (long long)" ep_gc_collect_minor();\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (ep_gc_count >= ep_gc_threshold) {\n"); + ok = append_list(lines, (long long)" ep_gc_collect_major();\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_start_the_world();\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Unregister an object (for explicit free — removes from GC tracking) */\n"); + ok = append_list(lines, (long long)"static void ep_gc_unregister(void* ptr) {\n"); + ok = append_list(lines, (long long)" if (!ptr) return;\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */\n"); + ok = append_list(lines, (long long)" /* Clean up references from the remembered set to prevent dangling pointers */\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < ep_gc_remembered_size; ) {\n"); + ok = append_list(lines, (long long)" if (ep_gc_remembered_set[i] == ptr) {\n"); + ok = append_list(lines, (long long)" for (long long j = i; j < ep_gc_remembered_size - 1; j++) {\n"); + ok = append_list(lines, (long long)" ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1];\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_remembered_size--;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" i++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_table_remove(ptr);\n"); + ok = append_list(lines, (long long)" EpGCObject** cur = &ep_gc_head;\n"); + ok = append_list(lines, (long long)" while (*cur) {\n"); + ok = append_list(lines, (long long)" if ((*cur)->ptr == ptr) {\n"); + ok = append_list(lines, (long long)" EpGCObject* found = *cur;\n"); + ok = append_list(lines, (long long)" *cur = found->next;\n"); + ok = append_list(lines, (long long)" if (found->generation == 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(found);\n"); + ok = append_list(lines, (long long)" ep_gc_count--;\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" return;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" cur = &(*cur)->next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Cleanup all remaining GC objects (called at program exit) */\n"); + ok = append_list(lines, (long long)"static void ep_gc_shutdown(void) {\n"); + ok = append_list(lines, (long long)" ep_gc_enabled = 0;\n"); + ok = append_list(lines, (long long)" /* Only free GC bookkeeping structures, not the tracked objects themselves.\n"); + ok = append_list(lines, (long long)" The RAII auto-cleanup has already freed owned objects, and the OS will\n"); + ok = append_list(lines, (long long)" reclaim everything else on process exit. Attempting to free individual\n"); + ok = append_list(lines, (long long)" objects here causes double-free aborts when RAII and GC both track\n"); + ok = append_list(lines, (long long)" the same allocation. */\n"); + ok = append_list(lines, (long long)" EpGCObject* cur = ep_gc_head;\n"); + ok = append_list(lines, (long long)" while (cur) {\n"); + ok = append_list(lines, (long long)" EpGCObject* next = cur->next;\n"); + ok = append_list(lines, (long long)" free(cur); /* free the GCObject wrapper only */\n"); + ok = append_list(lines, (long long)" cur = next;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_head = NULL;\n"); + ok = append_list(lines, (long long)" ep_gc_count = 0;\n"); + ok = append_list(lines, (long long)" if (ep_gc_table) {\n"); + ok = append_list(lines, (long long)" free(ep_gc_table);\n"); + ok = append_list(lines, (long long)" ep_gc_table = NULL;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_table_cap = 0;\n"); + ok = append_list(lines, (long long)" ep_gc_table_size = 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== End Garbage Collector ========== */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long create_list(void);\n"); + ok = append_list(lines, (long long)"long long append_list(long long list_ptr, long long value);\n"); + ok = append_list(lines, (long long)"long long get_list(long long list_ptr, long long index);\n"); + ok = append_list(lines, (long long)"long long set_list(long long list_ptr, long long index, long long value);\n"); + ok = append_list(lines, (long long)"long long length_list(long long list_ptr);\n"); + ok = append_list(lines, (long long)"long long free_list(long long list_ptr);\n"); + ok = append_list(lines, (long long)"long long pop_list(long long list_ptr);\n"); + ok = append_list(lines, (long long)"long long remove_list(long long list_ptr, long long index);\n"); + ok = append_list(lines, (long long)"char* string_from_list(long long list_ptr);\n"); + ok = append_list(lines, (long long)"long long string_to_list(const char* s);\n"); + ok = append_list(lines, (long long)"long long string_length(const char* s);\n"); + ok = append_list(lines, (long long)"long long display_string(const char* s);\n"); + ok = append_list(lines, (long long)"long long file_read(long long path_val);\n"); + ok = append_list(lines, (long long)"long long file_write(long long path_val, long long content_val);\n"); + ok = append_list(lines, (long long)"long long file_append(long long path_val, long long content_val);\n"); + ok = append_list(lines, (long long)"long long file_exists(long long path_val);\n"); + ok = append_list(lines, (long long)"long long string_contains(long long s_val, long long sub_val);\n"); + ok = append_list(lines, (long long)"long long string_index_of(long long s_val, long long sub_val);\n"); + ok = append_list(lines, (long long)"long long string_replace(long long s_val, long long old_val, long long new_val);\n"); + ok = append_list(lines, (long long)"long long string_upper(long long s_val);\n"); + ok = append_list(lines, (long long)"long long string_lower(long long s_val);\n"); + ok = append_list(lines, (long long)"long long string_trim(long long s_val);\n"); + ok = append_list(lines, (long long)"long long string_split(long long s_val, long long delim_val);\n"); + ok = append_list(lines, (long long)"long long char_at(long long s_val, long long index);\n"); + ok = append_list(lines, (long long)"long long char_from_code(long long code);\n"); + ok = append_list(lines, (long long)"long long ep_abs(long long n);\n"); + ok = append_list(lines, (long long)"long long json_get_string(long long json_val, long long key_val);\n"); + ok = append_list(lines, (long long)"long long json_get_int(long long json_val, long long key_val);\n"); + ok = append_list(lines, (long long)"long long json_get_bool(long long json_val, long long key_val);\n"); + ok = append_list(lines, (long long)"long long ep_sha1(long long data_val);\n"); + ok = append_list(lines, (long long)"long long ep_net_recv_bytes(long long fd, long long count);\n"); + ok = append_list(lines, (long long)"long long channel_try_recv(long long chan_ptr, long long out_ptr);\n"); + ok = append_list(lines, (long long)"long long channel_has_data(long long chan_ptr);\n"); + ok = append_list(lines, (long long)"long long channel_select(long long channels_list, long long timeout_ms);\n"); + ok = append_list(lines, (long long)"long long ep_auto_to_string(long long val);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct EpChannel_ {\n"); + ok = append_list(lines, (long long)" long long* data;\n"); + ok = append_list(lines, (long long)" long long capacity;\n"); + ok = append_list(lines, (long long)" long long head;\n"); + ok = append_list(lines, (long long)" long long tail;\n"); + ok = append_list(lines, (long long)" long long size;\n"); + ok = append_list(lines, (long long)" ep_mutex_t mutex;\n"); + ok = append_list(lines, (long long)" ep_cond_t cond_recv;\n"); + ok = append_list(lines, (long long)" ep_cond_t cond_send;\n"); + ok = append_list(lines, (long long)"} EpChannel;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Global channel registry — allows GC to scan values in-transit in channel buffers.\n"); + ok = append_list(lines, (long long)" Without this, an object sent to a channel but not yet received has NO GC root:\n"); + ok = append_list(lines, (long long)" the sender has popped it, the receiver hasn't pushed it, and the channel buffer\n"); + ok = append_list(lines, (long long)" is not scanned. The GC sweeps it → receiver gets a dangling pointer. */\n"); + ok = append_list(lines, (long long)"#define EP_MAX_CHANNELS 1024\n"); + ok = append_list(lines, (long long)"static EpChannel* ep_channel_registry[EP_MAX_CHANNELS];\n"); + ok = append_list(lines, (long long)"static int ep_channel_count = 0;\n"); + ok = append_list(lines, (long long)"static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_register_channel(EpChannel* chan) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)" if (ep_channel_count < EP_MAX_CHANNELS) {\n"); + ok = append_list(lines, (long long)" ep_channel_registry[ep_channel_count++] = chan;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Channel scanning implementations — called by GC mark via function pointers.\n"); + ok = append_list(lines, (long long)" These are defined here (after EpChannel) so they can access struct fields. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_object(void* ptr); /* forward decl */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_object_minor(void* ptr); /* forward decl */\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_12() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_scan_channels_major_impl(void) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)" for (int c = 0; c < ep_channel_count; c++) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = ep_channel_registry[c];\n"); + ok = append_list(lines, (long long)" if (!chan || chan->size <= 0) continue;\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" for (long long j = 0; j < chan->size; j++) {\n"); + ok = append_list(lines, (long long)" long long idx = (chan->head + j) % chan->capacity;\n"); + ok = append_list(lines, (long long)" long long val = chan->data[idx];\n"); + ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_scan_channels_minor_impl(void) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)" for (int c = 0; c < ep_channel_count; c++) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = ep_channel_registry[c];\n"); + ok = append_list(lines, (long long)" if (!chan || chan->size <= 0) continue;\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" for (long long j = 0; j < chan->size; j++) {\n"); + ok = append_list(lines, (long long)" long long idx = (chan->head + j) % chan->capacity;\n"); + ok = append_list(lines, (long long)" long long val = chan->data[idx];\n"); + ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object_minor((void*)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long create_channel(void) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = malloc(sizeof(EpChannel));\n"); + ok = append_list(lines, (long long)" if (!chan) return 0;\n"); + ok = append_list(lines, (long long)" chan->capacity = 1024;\n"); + ok = append_list(lines, (long long)" chan->data = malloc(chan->capacity * sizeof(long long));\n"); + ok = append_list(lines, (long long)" chan->head = 0;\n"); + ok = append_list(lines, (long long)" chan->tail = 0;\n"); + ok = append_list(lines, (long long)" chan->size = 0;\n"); + ok = append_list(lines, (long long)" ep_mutex_init(&chan->mutex);\n"); + ok = append_list(lines, (long long)" ep_cond_init(&chan->cond_recv);\n"); + ok = append_list(lines, (long long)" ep_cond_init(&chan->cond_send);\n"); + ok = append_list(lines, (long long)" ep_register_channel(chan);\n"); + ok = append_list(lines, (long long)" return (long long)chan;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long send_channel(long long chan_ptr, long long value) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)chan_ptr;\n"); + ok = append_list(lines, (long long)" if (!chan) return 0;\n"); + ok = append_list(lines, (long long)" /* Suppress GC during channel operations. The blocking condvar wait\n"); + ok = append_list(lines, (long long)" can interleave with GC mark/sweep on another thread, causing\n"); + ok = append_list(lines, (long long)" use-after-free when the GC sweeps objects that are live on a\n"); + ok = append_list(lines, (long long)" thread currently blocked in send/receive. Channel buffers contain\n"); + ok = append_list(lines, (long long)" raw long long values (not GC-tracked pointers), so suppressing\n"); + ok = append_list(lines, (long long)" GC here is safe. */\n"); + ok = append_list(lines, (long long)" int gc_was_enabled = ep_gc_enabled;\n"); + ok = append_list(lines, (long long)" ep_gc_enabled = 0;\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" while (chan->size >= chan->capacity) {\n"); + ok = append_list(lines, (long long)" ep_cond_wait(&chan->cond_send, &chan->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" chan->data[chan->tail] = value;\n"); + ok = append_list(lines, (long long)" chan->tail = (chan->tail + 1) % chan->capacity;\n"); + ok = append_list(lines, (long long)" chan->size += 1;\n"); + ok = append_list(lines, (long long)" ep_cond_signal(&chan->cond_recv);\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_enabled = gc_was_enabled;\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long receive_channel(long long chan_ptr) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)chan_ptr;\n"); + ok = append_list(lines, (long long)" if (!chan) return 0;\n"); + ok = append_list(lines, (long long)" /* Suppress GC during channel receive — same rationale as send_channel */\n"); + ok = append_list(lines, (long long)" int gc_was_enabled = ep_gc_enabled;\n"); + ok = append_list(lines, (long long)" ep_gc_enabled = 0;\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" while (chan->size <= 0) {\n"); + ok = append_list(lines, (long long)" ep_cond_wait(&chan->cond_recv, &chan->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" long long value = chan->data[chan->head];\n"); + ok = append_list(lines, (long long)" chan->head = (chan->head + 1) % chan->capacity;\n"); + ok = append_list(lines, (long long)" chan->size -= 1;\n"); + ok = append_list(lines, (long long)" ep_cond_signal(&chan->cond_send);\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_enabled = gc_was_enabled;\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// Non-blocking receive — returns 1 if data was available, 0 if channel empty\n"); + ok = append_list(lines, (long long)"long long channel_try_recv(long long chan_ptr, long long out_ptr) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)chan_ptr;\n"); + ok = append_list(lines, (long long)" if (!chan) return 0;\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" if (chan->size <= 0) {\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" long long value = chan->data[chan->head];\n"); + ok = append_list(lines, (long long)" chan->head = (chan->head + 1) % chan->capacity;\n"); + ok = append_list(lines, (long long)" chan->size -= 1;\n"); + ok = append_list(lines, (long long)" ep_cond_signal(&chan->cond_send);\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" if (out_ptr) {\n"); + ok = append_list(lines, (long long)" *((long long*)out_ptr) = value;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// Check if channel has data without consuming it\n"); + ok = append_list(lines, (long long)"long long channel_has_data(long long chan_ptr) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)chan_ptr;\n"); + ok = append_list(lines, (long long)" if (!chan) return 0;\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" int has = (chan->size > 0) ? 1 : 0;\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" return has;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// Select: wait for any of N channels to have data, with timeout in ms\n"); + ok = append_list(lines, (long long)"// channels_list is a list of channel pointers\n"); + ok = append_list(lines, (long long)"// Returns index (0-based) of first ready channel, or -1 on timeout\n"); + ok = append_list(lines, (long long)"long long channel_select(long long channels_list, long long timeout_ms) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)channels_list;\n"); + ok = append_list(lines, (long long)" if (!list || list->length == 0) return -1;\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" ULONGLONG start_tick = GetTickCount64();\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" struct timespec start, now;\n"); + ok = append_list(lines, (long long)" clock_gettime(CLOCK_MONOTONIC, &start);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" while (1) {\n"); + ok = append_list(lines, (long long)" // Poll all channels\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n"); + ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)list->data[i];\n"); + ok = append_list(lines, (long long)" if (chan) {\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" if (chan->size > 0) {\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" return i;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" // Check timeout\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_13() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" if (timeout_ms >= 0) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" ULONGLONG now_tick = GetTickCount64();\n"); + ok = append_list(lines, (long long)" long long elapsed = (long long)(now_tick - start_tick);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" clock_gettime(CLOCK_MONOTONIC, &now);\n"); + ok = append_list(lines, (long long)" long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000;\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" if (elapsed >= timeout_ms) return -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" // Brief sleep to avoid busy-wait\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" Sleep(1);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" usleep(1000); // 1ms\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)"long long ep_net_connect(const char* host, long long port) {\n"); + ok = append_list(lines, (long long)" (void)host; (void)port;\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_listen(long long port) {\n"); + ok = append_list(lines, (long long)" (void)port;\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_accept(long long server_fd) {\n"); + ok = append_list(lines, (long long)" (void)server_fd;\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_send(long long fd, const char* data) {\n"); + ok = append_list(lines, (long long)" (void)fd; (void)data;\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* ep_net_recv(long long fd, long long max_len) {\n"); + ok = append_list(lines, (long long)" (void)fd; (void)max_len;\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_close(long long fd) {\n"); + ok = append_list(lines, (long long)" (void)fd;\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sleep_ms(long long ms) {\n"); + ok = append_list(lines, (long long)" struct timespec ts;\n"); + ok = append_list(lines, (long long)" ts.tv_sec = ms / 1000;\n"); + ok = append_list(lines, (long long)" ts.tv_nsec = (ms % 1000) * 1000000;\n"); + ok = append_list(lines, (long long)" nanosleep(&ts, NULL);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_system(long long cmd) {\n"); + ok = append_list(lines, (long long)" (void)cmd;\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_play_sound(long long path) {\n"); + ok = append_list(lines, (long long)" (void)path;\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlopen(long long path) {\n"); + ok = append_list(lines, (long long)" (void)path;\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlsym(long long handle, long long name) {\n"); + ok = append_list(lines, (long long)" (void)handle; (void)name;\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlclose(long long handle) {\n"); + ok = append_list(lines, (long long)" (void)handle;\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"long long ep_net_connect(const char* host, long long port) {\n"); + ok = append_list(lines, (long long)" int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n"); + ok = append_list(lines, (long long)" if (sockfd < 0) return -1;\n"); + ok = append_list(lines, (long long)" struct hostent* server = gethostbyname(host);\n"); + ok = append_list(lines, (long long)" if (!server) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" closesocket(sockfd);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n"); + ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n"); + ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n"); + ok = append_list(lines, (long long)" memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n"); + ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n"); + ok = append_list(lines, (long long)" closesocket(sockfd);\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" // Bounded connect: an unreachable peer must not block ~75s on the OS SYN\n"); + ok = append_list(lines, (long long)" // timeout (this stalled node startup). Non-blocking connect + 5s select, then\n"); + ok = append_list(lines, (long long)" // restore blocking mode for the rest of the session.\n"); + ok = append_list(lines, (long long)" int _ep_flags = fcntl(sockfd, F_GETFL, 0);\n"); + ok = append_list(lines, (long long)" fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK);\n"); + ok = append_list(lines, (long long)" int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));\n"); + ok = append_list(lines, (long long)" if (_ep_cr < 0) {\n"); + ok = append_list(lines, (long long)" if (errno != EINPROGRESS) { close(sockfd); return -1; }\n"); + ok = append_list(lines, (long long)" fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset);\n"); + ok = append_list(lines, (long long)" struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0;\n"); + ok = append_list(lines, (long long)" int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv);\n"); + ok = append_list(lines, (long long)" if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error\n"); + ok = append_list(lines, (long long)" int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err);\n"); + ok = append_list(lines, (long long)" if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" fcntl(sockfd, F_SETFL, _ep_flags);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" return sockfd;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_listen(long long port) {\n"); + ok = append_list(lines, (long long)" int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n"); + ok = append_list(lines, (long long)" if (sockfd < 0) return -1;\n"); + ok = append_list(lines, (long long)" int opt = 1;\n"); + ok = append_list(lines, (long long)" setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n"); + ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n"); + ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n"); + ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n"); + ok = append_list(lines, (long long)" serv_addr.sin_addr.s_addr = INADDR_ANY;\n"); + ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n"); + ok = append_list(lines, (long long)" if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" closesocket(sockfd);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_14() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" if (listen(sockfd, 10) < 0) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" closesocket(sockfd);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return sockfd;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_accept(long long server_fd) {\n"); + ok = append_list(lines, (long long)" struct sockaddr_in cli_addr;\n"); + ok = append_list(lines, (long long)" socklen_t clilen = sizeof(cli_addr);\n"); + ok = append_list(lines, (long long)" int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);\n"); + ok = append_list(lines, (long long)" if (newsockfd >= 0) {\n"); + ok = append_list(lines, (long long)" /* Bound how long a single recv/send may block so a slow or silent\n"); + ok = append_list(lines, (long long)" client cannot pin a handler thread forever (slowloris). */\n"); + ok = append_list(lines, (long long)" struct timeval tv;\n"); + ok = append_list(lines, (long long)" tv.tv_sec = 30;\n"); + ok = append_list(lines, (long long)" tv.tv_usec = 0;\n"); + ok = append_list(lines, (long long)" setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));\n"); + ok = append_list(lines, (long long)" setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return newsockfd;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_send(long long fd, const char* data) {\n"); + ok = append_list(lines, (long long)" if (!data) return 0;\n"); + ok = append_list(lines, (long long)" /* send() may write fewer bytes than requested (partial write under load/\n"); + ok = append_list(lines, (long long)" backpressure). A single send() therefore silently truncated large IPC\n"); + ok = append_list(lines, (long long)" responses, cutting agent replies mid-stream. Loop until all bytes are sent. */\n"); + ok = append_list(lines, (long long)" size_t total = strlen(data);\n"); + ok = append_list(lines, (long long)" size_t off = 0;\n"); + ok = append_list(lines, (long long)" while (off < total) {\n"); + ok = append_list(lines, (long long)" ssize_t n = send((int)fd, data + off, total - off, 0);\n"); + ok = append_list(lines, (long long)" if (n <= 0) break;\n"); + ok = append_list(lines, (long long)" off += (size_t)n;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return (long long)off;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* ep_net_recv(long long fd, long long max_len) {\n"); + ok = append_list(lines, (long long)" char* buf = malloc(max_len + 1);\n"); + ok = append_list(lines, (long long)" if (!buf) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" int n = recv((int)fd, buf, (int)max_len, 0);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" ssize_t n = recv((int)fd, buf, max_len, 0);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" if (n < 0) n = 0;\n"); + ok = append_list(lines, (long long)" buf[n] = '\\0';\n"); + ok = append_list(lines, (long long)" return buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_net_close(long long fd) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" return closesocket((int)fd);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" return close((int)fd);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sleep_ms(long long ms) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" Sleep((DWORD)ms);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" usleep((useconds_t)(ms * 1000));\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_system(long long cmd) {\n"); + ok = append_list(lines, (long long)" return (long long)system((const char*)cmd);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_play_sound(long long path) {\n"); + ok = append_list(lines, (long long)" char cmd[512];\n"); + ok = append_list(lines, (long long)" snprintf(cmd, sizeof(cmd), \"afplay '%s' &\", (const char*)path);\n"); + ok = append_list(lines, (long long)" return (long long)system(cmd);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Dynamic Library Loading (FFI) ========== */\n"); + ok = append_list(lines, (long long)"#ifndef _WIN32\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlopen(long long path) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" HMODULE h = LoadLibraryA((const char*)path);\n"); + ok = append_list(lines, (long long)" return (long long)h;\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" const char* p = (const char*)path;\n"); + ok = append_list(lines, (long long)" void* handle = dlopen(p, RTLD_LAZY);\n"); + ok = append_list(lines, (long long)" return (long long)handle;\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlsym(long long handle, long long name) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name);\n"); + ok = append_list(lines, (long long)" return (long long)sym;\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" void* sym = dlsym((void*)handle, (const char*)name);\n"); + ok = append_list(lines, (long long)" return (long long)sym;\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlclose(long long handle) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" return (long long)FreeLibrary((HMODULE)handle);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" return (long long)dlclose((void*)handle);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Call a function pointer with 0..6 arguments.\n"); + ok = append_list(lines, (long long)" These are type-punned through long long — the C calling convention\n"); + ok = append_list(lines, (long long)" makes this work for integer and pointer arguments. */\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn0)(void);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn1)(long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn2)(long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn3)(long long, long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn4)(long long, long long, long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn5)(long long, long long, long long, long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlcall0(long long fptr) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn0)fptr)();\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall1(long long fptr, long long a0) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn1)fptr)(a0);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall2(long long fptr, long long a0, long long a1) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn2)fptr)(a0, a1);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn3)fptr)(a0, a1, a2);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn4)fptr)(a0, a1, a2, a3);\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_15() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn5)fptr)(a0, a1, a2, a3, a4);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Float FFI: ep_dlcall_f* ========== */\n"); + ok = append_list(lines, (long long)"/* For calling C functions that accept/return double values.\n"); + ok = append_list(lines, (long long)" Arguments are passed as long long (bit-punned doubles).\n"); + ok = append_list(lines, (long long)" Return value is a double bit-punned back to long long.\n"); + ok = append_list(lines, (long long)" Use ep_double_to_bits() / ep_bits_to_double() to convert. */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef union { long long i; double f; } ep_float_bits;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static inline double ep_ll_to_double(long long v) {\n"); + ok = append_list(lines, (long long)" ep_float_bits u; u.i = v; return u.f;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"static inline long long ep_double_to_ll(double v) {\n"); + ok = append_list(lines, (long long)" ep_float_bits u; u.f = v; return u.i;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Convert between ErnosPlain float representation and raw bits */\n"); + ok = append_list(lines, (long long)"long long ep_double_to_bits(long long float_val) {\n"); + ok = append_list(lines, (long long)" /* float_val is already an EP Float stored as long long bits */\n"); + ok = append_list(lines, (long long)" return float_val;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_bits_to_double(long long bits) {\n"); + ok = append_list(lines, (long long)" return bits;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Float function pointer typedefs */\n"); + ok = append_list(lines, (long long)"typedef double (*ep_ff0)(void);\n"); + ok = append_list(lines, (long long)"typedef double (*ep_ff1)(double);\n"); + ok = append_list(lines, (long long)"typedef double (*ep_ff2)(double, double);\n"); + ok = append_list(lines, (long long)"typedef double (*ep_ff3)(double, double, double);\n"); + ok = append_list(lines, (long long)"typedef double (*ep_ff4)(double, double, double, double);\n"); + ok = append_list(lines, (long long)"typedef double (*ep_ff5)(double, double, double, double, double);\n"); + ok = append_list(lines, (long long)"typedef double (*ep_ff6)(double, double, double, double, double, double);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Call functions that take doubles and return double */\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_f0(long long fptr) {\n"); + ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff0)fptr)());\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_f1(long long fptr, long long a0) {\n"); + ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0)));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_f2(long long fptr, long long a0, long long a1) {\n"); + ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) {\n"); + ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n"); + ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3)));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n"); + ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff5)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4)));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_f6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {\n"); + ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff6)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4), ep_ll_to_double(a5)));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Variants that take doubles but return int (for comparison functions etc.) */\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fdi1)(double);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fdi2)(double, double);\n"); + ok = append_list(lines, (long long)"typedef long long (*ep_fdi3)(double, double, double);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_fd1(long long fptr, long long a0) {\n"); + ok = append_list(lines, (long long)" return ((ep_fdi1)fptr)(ep_ll_to_double(a0));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) {\n"); + ok = append_list(lines, (long long)" return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) {\n"); + ok = append_list(lines, (long long)" return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"/* ========== End Float FFI ========== */\n"); + ok = append_list(lines, (long long)"/* ========== End Dynamic Library Loading ========== */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"unsigned long hash_string(const char* str) {\n"); + ok = append_list(lines, (long long)" unsigned long hash = 5381;\n"); + ok = append_list(lines, (long long)" int c;\n"); + ok = append_list(lines, (long long)" while ((c = *str++)) {\n"); + ok = append_list(lines, (long long)" hash = ((hash << 5) + hash) + c;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return hash;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" char* key;\n"); + ok = append_list(lines, (long long)" long long value;\n"); + ok = append_list(lines, (long long)" int used;\n"); + ok = append_list(lines, (long long)"} EpMapEntry;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" EpMapEntry* entries;\n"); + ok = append_list(lines, (long long)" long long capacity;\n"); + ok = append_list(lines, (long long)" long long size;\n"); + ok = append_list(lines, (long long)"} EpMap;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Map value traversal for GC — walks all entries and marks values.\n"); + ok = append_list(lines, (long long)" Called by ep_gc_mark_object() via function pointer. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_map_values_impl(void* ptr) {\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)ptr;\n"); + ok = append_list(lines, (long long)" if (!map || !map->entries) return;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n"); + ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].value != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)map->entries[i].value);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Also mark keys if they are heap strings */\n"); + ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object((void*)map->entries[i].key);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_map_values_minor_impl(void* ptr) {\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)ptr;\n"); + ok = append_list(lines, (long long)" if (!map || !map->entries) return;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n"); + ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].value != 0) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)map->entries[i].value);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)map->entries[i].key);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long create_map(void) {\n"); + ok = append_list(lines, (long long)" EpMap* map = malloc(sizeof(EpMap));\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" map->capacity = 16;\n"); + ok = append_list(lines, (long long)" map->size = 0;\n"); + ok = append_list(lines, (long long)" map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n"); + ok = append_list(lines, (long long)" if (!map->entries) {\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_16() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" free(map);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_register(map, EP_OBJ_MAP);\n"); + ok = append_list(lines, (long long)" return (long long)map;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void map_resize(EpMap* map, long long new_capacity) {\n"); + ok = append_list(lines, (long long)" EpMapEntry* old_entries = map->entries;\n"); + ok = append_list(lines, (long long)" long long old_capacity = map->capacity;\n"); + ok = append_list(lines, (long long)" map->capacity = new_capacity;\n"); + ok = append_list(lines, (long long)" map->entries = calloc(new_capacity, sizeof(EpMapEntry));\n"); + ok = append_list(lines, (long long)" map->size = 0;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < old_capacity; i++) {\n"); + ok = append_list(lines, (long long)" if (old_entries[i].used && old_entries[i].key != NULL) {\n"); + ok = append_list(lines, (long long)" char* key = old_entries[i].key;\n"); + ok = append_list(lines, (long long)" long long value = old_entries[i].value;\n"); + ok = append_list(lines, (long long)" unsigned long h = hash_string(key) % new_capacity;\n"); + ok = append_list(lines, (long long)" while (map->entries[h].used) {\n"); + ok = append_list(lines, (long long)" h = (h + 1) % new_capacity;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" map->entries[h].key = key;\n"); + ok = append_list(lines, (long long)" map->entries[h].value = value;\n"); + ok = append_list(lines, (long long)" map->entries[h].used = 1;\n"); + ok = append_list(lines, (long long)" map->size++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(old_entries);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Convert a key value to a string — handles both string pointers and integers */\n"); + ok = append_list(lines, (long long)"static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) {\n"); + ok = append_list(lines, (long long)" if (key_val == 0) { buf[0] = '0'; buf[1] = '\\0'; return buf; }\n"); + ok = append_list(lines, (long long)" /* Check if value is in plausible pointer range for a string */\n"); + ok = append_list(lines, (long long)" if (key_val > 0x100000) {\n"); + ok = append_list(lines, (long long)" const char* p = (const char*)(void*)key_val;\n"); + ok = append_list(lines, (long long)" unsigned char first = (unsigned char)*p;\n"); + ok = append_list(lines, (long long)" if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) {\n"); + ok = append_list(lines, (long long)" return p; /* valid string pointer */\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" snprintf(buf, bufsize, \"%lld\", key_val);\n"); + ok = append_list(lines, (long long)" return buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long map_insert(long long map_ptr, long long key_val, long long value) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(map_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" char keybuf[32];\n"); + ok = append_list(lines, (long long)" const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" if (map->size * 2 >= map->capacity) {\n"); + ok = append_list(lines, (long long)" map_resize(map, map->capacity * 2);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" unsigned long h = hash_string(key) % map->capacity;\n"); + ok = append_list(lines, (long long)" while (map->entries[h].used) {\n"); + ok = append_list(lines, (long long)" if (strcmp(map->entries[h].key, key) == 0) {\n"); + ok = append_list(lines, (long long)" map->entries[h].value = value;\n"); + ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)map_ptr, value);\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" map->entries[h].key = strdup(key);\n"); + ok = append_list(lines, (long long)" map->entries[h].value = value;\n"); + ok = append_list(lines, (long long)" map->entries[h].used = 1;\n"); + ok = append_list(lines, (long long)" map->size++;\n"); + ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)map_ptr, value);\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long map_get_val(long long map_ptr, long long key_val) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(map_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" char keybuf[32];\n"); + ok = append_list(lines, (long long)" const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" unsigned long h = hash_string(key) % map->capacity;\n"); + ok = append_list(lines, (long long)" long long start_h = h;\n"); + ok = append_list(lines, (long long)" while (map->entries[h].used) {\n"); + ok = append_list(lines, (long long)" if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n"); + ok = append_list(lines, (long long)" return map->entries[h].value;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" if (h == start_h) break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* map_set_str: store a string value (strdup'd copy) under a string key */\n"); + ok = append_list(lines, (long long)"long long map_set_str(long long map_ptr, long long key_val, long long str_val) {\n"); + ok = append_list(lines, (long long)" /* Store the string pointer as a long long value — same as map_insert */\n"); + ok = append_list(lines, (long long)" return map_insert(map_ptr, key_val, str_val);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* map_get_str: retrieve a string value from a map (returns char* as long long) */\n"); + ok = append_list(lines, (long long)"long long map_get_str(long long map_ptr, long long key_val) {\n"); + ok = append_list(lines, (long long)" /* Same as map_get_val — the stored long long IS a char* pointer */\n"); + ok = append_list(lines, (long long)" return map_get_val(map_ptr, key_val);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long map_contains(long long map_ptr, long long key_val) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(map_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" char keybuf[32];\n"); + ok = append_list(lines, (long long)" const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" unsigned long h = hash_string(key) % map->capacity;\n"); + ok = append_list(lines, (long long)" long long start_h = h;\n"); + ok = append_list(lines, (long long)" while (map->entries[h].used) {\n"); + ok = append_list(lines, (long long)" if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" if (h == start_h) break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long map_delete(long long map_ptr, long long key_val) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(map_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" char keybuf[32];\n"); + ok = append_list(lines, (long long)" const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" unsigned long h = hash_string(key) % map->capacity;\n"); + ok = append_list(lines, (long long)" long long start_h = h;\n"); + ok = append_list(lines, (long long)" while (map->entries[h].used) {\n"); + ok = append_list(lines, (long long)" if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n"); + ok = append_list(lines, (long long)" free(map->entries[h].key);\n"); + ok = append_list(lines, (long long)" map->entries[h].key = NULL;\n"); + ok = append_list(lines, (long long)" map->entries[h].value = 0;\n"); + ok = append_list(lines, (long long)" map->entries[h].used = 0;\n"); + ok = append_list(lines, (long long)" map->size--;\n"); + ok = append_list(lines, (long long)" long long next_h = (h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" while (map->entries[next_h].used) {\n"); + ok = append_list(lines, (long long)" char* k = map->entries[next_h].key;\n"); + ok = append_list(lines, (long long)" long long v = map->entries[next_h].value;\n"); + ok = append_list(lines, (long long)" map->entries[next_h].key = NULL;\n"); + ok = append_list(lines, (long long)" map->entries[next_h].value = 0;\n"); + ok = append_list(lines, (long long)" map->entries[next_h].used = 0;\n"); + ok = append_list(lines, (long long)" map->size--;\n"); + ok = append_list(lines, (long long)" map_insert(map_ptr, (long long)k, v);\n"); + ok = append_list(lines, (long long)" free(k);\n"); + ok = append_list(lines, (long long)" next_h = (next_h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" if (h == start_h) break;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_17() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long map_keys(long long map_ptr) {\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" if (!map) return (long long)create_list();\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n"); + ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key) {\n"); + ok = append_list(lines, (long long)" append_list(list, (long long)strdup(map->entries[i].key));\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long map_values(long long map_ptr) {\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" if (!map) return (long long)create_list();\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n"); + ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key) {\n"); + ok = append_list(lines, (long long)" append_list(list, map->entries[i].value);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long map_size(long long map_ptr) {\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" return map->size;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long free_map(long long map_ptr) {\n"); + ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" /* Skip if already freed (idempotent) */\n"); + ok = append_list(lines, (long long)" if (!ep_gc_find(map)) return 0;\n"); + ok = append_list(lines, (long long)" ep_gc_unregister(map);\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n"); + ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\n"); + ok = append_list(lines, (long long)" free(map->entries[i].key);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(map->entries);\n"); + ok = append_list(lines, (long long)" free(map);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" long long* data;\n"); + ok = append_list(lines, (long long)" long long capacity;\n"); + ok = append_list(lines, (long long)" long long head;\n"); + ok = append_list(lines, (long long)" long long tail;\n"); + ok = append_list(lines, (long long)" long long size;\n"); + ok = append_list(lines, (long long)"} EpDeque;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long create_deque(void) {\n"); + ok = append_list(lines, (long long)" EpDeque* dq = malloc(sizeof(EpDeque));\n"); + ok = append_list(lines, (long long)" if (!dq) return 0;\n"); + ok = append_list(lines, (long long)" dq->capacity = 16;\n"); + ok = append_list(lines, (long long)" dq->size = 0;\n"); + ok = append_list(lines, (long long)" dq->head = 0;\n"); + ok = append_list(lines, (long long)" dq->tail = 0;\n"); + ok = append_list(lines, (long long)" dq->data = malloc(dq->capacity * sizeof(long long));\n"); + ok = append_list(lines, (long long)" if (!dq->data) {\n"); + ok = append_list(lines, (long long)" free(dq);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return (long long)dq;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void deque_resize(EpDeque* dq, long long new_capacity) {\n"); + ok = append_list(lines, (long long)" long long* new_data = malloc(new_capacity * sizeof(long long));\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < dq->size; i++) {\n"); + ok = append_list(lines, (long long)" new_data[i] = dq->data[(dq->head + i) % dq->capacity];\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(dq->data);\n"); + ok = append_list(lines, (long long)" dq->data = new_data;\n"); + ok = append_list(lines, (long long)" dq->capacity = new_capacity;\n"); + ok = append_list(lines, (long long)" dq->head = 0;\n"); + ok = append_list(lines, (long long)" dq->tail = dq->size;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long deque_push_back(long long dq_ptr, long long value) {\n"); + ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n"); + ok = append_list(lines, (long long)" if (!dq) return 0;\n"); + ok = append_list(lines, (long long)" if (dq->size >= dq->capacity) {\n"); + ok = append_list(lines, (long long)" deque_resize(dq, dq->capacity * 2);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" dq->data[dq->tail] = value;\n"); + ok = append_list(lines, (long long)" dq->tail = (dq->tail + 1) % dq->capacity;\n"); + ok = append_list(lines, (long long)" dq->size++;\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long deque_push_front(long long dq_ptr, long long value) {\n"); + ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n"); + ok = append_list(lines, (long long)" if (!dq) return 0;\n"); + ok = append_list(lines, (long long)" if (dq->size >= dq->capacity) {\n"); + ok = append_list(lines, (long long)" deque_resize(dq, dq->capacity * 2);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;\n"); + ok = append_list(lines, (long long)" dq->data[dq->head] = value;\n"); + ok = append_list(lines, (long long)" dq->size++;\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long deque_pop_back(long long dq_ptr) {\n"); + ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n"); + ok = append_list(lines, (long long)" if (!dq || dq->size == 0) return 0;\n"); + ok = append_list(lines, (long long)" dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;\n"); + ok = append_list(lines, (long long)" long long value = dq->data[dq->tail];\n"); + ok = append_list(lines, (long long)" dq->size--;\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long deque_pop_front(long long dq_ptr) {\n"); + ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n"); + ok = append_list(lines, (long long)" if (!dq || dq->size == 0) return 0;\n"); + ok = append_list(lines, (long long)" long long value = dq->data[dq->head];\n"); + ok = append_list(lines, (long long)" dq->head = (dq->head + 1) % dq->capacity;\n"); + ok = append_list(lines, (long long)" dq->size--;\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long deque_length(long long dq_ptr) {\n"); + ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n"); + ok = append_list(lines, (long long)" if (!dq) return 0;\n"); + ok = append_list(lines, (long long)" return dq->size;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long free_deque(long long dq_ptr) {\n"); + ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n"); + ok = append_list(lines, (long long)" if (!dq) return 0;\n"); + ok = append_list(lines, (long long)" free(dq->data);\n"); + ok = append_list(lines, (long long)" free(dq);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Filesystem Operations */\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_scan_dir(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" long long list_ptr = create_list();\n"); + ok = append_list(lines, (long long)" if (!path) return list_ptr;\n"); + ok = append_list(lines, (long long)" DIR* d = opendir(path);\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_18() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" if (!d) return list_ptr;\n"); + ok = append_list(lines, (long long)" struct dirent* dir;\n"); + ok = append_list(lines, (long long)" while ((dir = readdir(d)) != NULL) {\n"); + ok = append_list(lines, (long long)" if (strcmp(dir->d_name, \".\") == 0 || strcmp(dir->d_name, \"..\") == 0) {\n"); + ok = append_list(lines, (long long)" continue;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" char* name = strdup(dir->d_name);\n"); + ok = append_list(lines, (long long)" append_list(list_ptr, (long long)name);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" closedir(d);\n"); + ok = append_list(lines, (long long)" return list_ptr;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_copy_file(long long src_val, long long dest_val) {\n"); + ok = append_list(lines, (long long)" const char* src = (const char*)src_val;\n"); + ok = append_list(lines, (long long)" const char* dest = (const char*)dest_val;\n"); + ok = append_list(lines, (long long)" if (!src || !dest) return 0;\n"); + ok = append_list(lines, (long long)" FILE* f_src = fopen(src, \"rb\");\n"); + ok = append_list(lines, (long long)" if (!f_src) return 0;\n"); + ok = append_list(lines, (long long)" FILE* f_dest = fopen(dest, \"wb\");\n"); + ok = append_list(lines, (long long)" if (!f_dest) {\n"); + ok = append_list(lines, (long long)" fclose(f_src);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" char buf[4096];\n"); + ok = append_list(lines, (long long)" size_t n;\n"); + ok = append_list(lines, (long long)" while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) {\n"); + ok = append_list(lines, (long long)" fwrite(buf, 1, n, f_dest);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" fclose(f_src);\n"); + ok = append_list(lines, (long long)" fclose(f_dest);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_delete_file(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" if (!path) return 0;\n"); + ok = append_list(lines, (long long)" return remove(path) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_move_file(long long src_val, long long dest_val) {\n"); + ok = append_list(lines, (long long)" const char* src = (const char*)src_val;\n"); + ok = append_list(lines, (long long)" const char* dest = (const char*)dest_val;\n"); + ok = append_list(lines, (long long)" if (!src || !dest) return 0;\n"); + ok = append_list(lines, (long long)" return rename(src, dest) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_exists(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" if (!path) return 0;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_is_dir(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" if (!path) return 0;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n"); + ok = append_list(lines, (long long)" return S_ISDIR(st.st_mode) ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_is_file(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" if (!path) return 0;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n"); + ok = append_list(lines, (long long)" return S_ISREG(st.st_mode) ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_get_size(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" if (!path) return 0;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n"); + ok = append_list(lines, (long long)" return (long long)st.st_size;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* HTTP Client */\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)"long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n"); + ok = append_list(lines, (long long)" (void)method_val; (void)url_val; (void)headers_val; (void)body_val;\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: HTTP request is not supported on WebAssembly\");\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n"); + ok = append_list(lines, (long long)" const char* method = (const char*)method_val;\n"); + ok = append_list(lines, (long long)" const char* url = (const char*)url_val;\n"); + ok = append_list(lines, (long long)" const char* headers = (const char*)headers_val;\n"); + ok = append_list(lines, (long long)" const char* body = (const char*)body_val;\n"); + ok = append_list(lines, (long long)" if (!method || !url) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" if (strncmp(url, \"http://\", 7) != 0) {\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: only http:// protocol supported\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" const char* host_start = url + 7;\n"); + ok = append_list(lines, (long long)" const char* path_start = strchr(host_start, '/');\n"); + ok = append_list(lines, (long long)" char host[256];\n"); + ok = append_list(lines, (long long)" char path[1024];\n"); + ok = append_list(lines, (long long)" if (path_start) {\n"); + ok = append_list(lines, (long long)" size_t host_len = path_start - host_start;\n"); + ok = append_list(lines, (long long)" if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;\n"); + ok = append_list(lines, (long long)" strncpy(host, host_start, host_len);\n"); + ok = append_list(lines, (long long)" host[host_len] = '\\0';\n"); + ok = append_list(lines, (long long)" strncpy(path, path_start, sizeof(path) - 1);\n"); + ok = append_list(lines, (long long)" path[sizeof(path) - 1] = '\\0';\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" strncpy(host, host_start, sizeof(host) - 1);\n"); + ok = append_list(lines, (long long)" host[sizeof(host) - 1] = '\\0';\n"); + ok = append_list(lines, (long long)" strcpy(path, \"/\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" int port = 80;\n"); + ok = append_list(lines, (long long)" char* colon = strchr(host, ':');\n"); + ok = append_list(lines, (long long)" if (colon) {\n"); + ok = append_list(lines, (long long)" *colon = '\\0';\n"); + ok = append_list(lines, (long long)" port = atoi(colon + 1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n"); + ok = append_list(lines, (long long)" if (sockfd < 0) return (long long)strdup(\"Error: socket creation failed\");\n"); + ok = append_list(lines, (long long)" struct hostent* server = gethostbyname(host);\n"); + ok = append_list(lines, (long long)" if (!server) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: host resolution failed\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n"); + ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n"); + ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n"); + ok = append_list(lines, (long long)" memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n"); + ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n"); + ok = append_list(lines, (long long)" if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: connection failed\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" char req[4096];\n"); + ok = append_list(lines, (long long)" size_t body_len = body ? strlen(body) : 0;\n"); + ok = append_list(lines, (long long)" int req_len = snprintf(req, sizeof(req),\n"); + ok = append_list(lines, (long long)" \"%s %s HTTP/1.1\\r\\n\"\n"); + ok = append_list(lines, (long long)" \"Host: %s\\r\\n\"\n"); + ok = append_list(lines, (long long)" \"Content-Length: %zu\\r\\n\"\n"); + ok = append_list(lines, (long long)" \"Connection: close\\r\\n\"\n"); + ok = append_list(lines, (long long)" \"%s%s\"\n"); + ok = append_list(lines, (long long)" \"\\r\\n\",\n"); + ok = append_list(lines, (long long)" method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n"); + ok = append_list(lines, (long long)" if (send(sockfd, req, req_len, 0) < 0) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send failed\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (body_len > 0) {\n"); + ok = append_list(lines, (long long)" if (send(sockfd, body, body_len, 0) < 0) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send body failed\");\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_19() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" size_t resp_cap = 4096;\n"); + ok = append_list(lines, (long long)" size_t resp_len = 0;\n"); + ok = append_list(lines, (long long)" char* resp = malloc(resp_cap);\n"); + ok = append_list(lines, (long long)" if (!resp) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" char recv_buf[4096];\n"); + ok = append_list(lines, (long long)" ssize_t n;\n"); + ok = append_list(lines, (long long)" while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {\n"); + ok = append_list(lines, (long long)" if (resp_len + n >= resp_cap) {\n"); + ok = append_list(lines, (long long)" resp_cap *= 2;\n"); + ok = append_list(lines, (long long)" char* new_resp = realloc(resp, resp_cap);\n"); + ok = append_list(lines, (long long)" if (!new_resp) {\n"); + ok = append_list(lines, (long long)" free(resp);\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: memory allocation failed\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" resp = new_resp;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" memcpy(resp + resp_len, recv_buf, n);\n"); + ok = append_list(lines, (long long)" resp_len += n;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" resp[resp_len] = '\\0';\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" // Strip HTTP headers — return only the body after \\r\\n\\r\\n\n"); + ok = append_list(lines, (long long)" char* http_body = strstr(resp, \"\\r\\n\\r\\n\");\n"); + ok = append_list(lines, (long long)" if (http_body) {\n"); + ok = append_list(lines, (long long)" http_body += 4;\n"); + ok = append_list(lines, (long long)" char* result = strdup(http_body);\n"); + ok = append_list(lines, (long long)" free(resp);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return (long long)resp;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))\n"); + ok = append_list(lines, (long long)"#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n"); + ok = append_list(lines, (long long)"#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n"); + ok = append_list(lines, (long long)"#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n"); + ok = append_list(lines, (long long)"#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n"); + ok = append_list(lines, (long long)"#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n"); + ok = append_list(lines, (long long)"#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" unsigned char data[64];\n"); + ok = append_list(lines, (long long)" unsigned int datalen;\n"); + ok = append_list(lines, (long long)" unsigned long long bitlen;\n"); + ok = append_list(lines, (long long)" unsigned int state[8];\n"); + ok = append_list(lines, (long long)"} EP_SHA256_CTX;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static const unsigned int sha256_k[64] = {\n"); + ok = append_list(lines, (long long)" 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n"); + ok = append_list(lines, (long long)" 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n"); + ok = append_list(lines, (long long)" 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n"); + ok = append_list(lines, (long long)" 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n"); + ok = append_list(lines, (long long)" 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n"); + ok = append_list(lines, (long long)" 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n"); + ok = append_list(lines, (long long)" 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n"); + ok = append_list(lines, (long long)" 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n"); + ok = append_list(lines, (long long)"};\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {\n"); + ok = append_list(lines, (long long)" unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n"); + ok = append_list(lines, (long long)" for (i = 0, j = 0; i < 16; ++i, j += 4)\n"); + ok = append_list(lines, (long long)" m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n"); + ok = append_list(lines, (long long)" for ( ; i < 64; ++i)\n"); + ok = append_list(lines, (long long)" m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n"); + ok = append_list(lines, (long long)" a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];\n"); + ok = append_list(lines, (long long)" e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];\n"); + ok = append_list(lines, (long long)" for (i = 0; i < 64; ++i) {\n"); + ok = append_list(lines, (long long)" t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];\n"); + ok = append_list(lines, (long long)" t2 = EP0(a) + MAJ(a,b,c);\n"); + ok = append_list(lines, (long long)" h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;\n"); + ok = append_list(lines, (long long)" ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_sha256_init(EP_SHA256_CTX *ctx) {\n"); + ok = append_list(lines, (long long)" ctx->datalen = 0; ctx->bitlen = 0;\n"); + ok = append_list(lines, (long long)" ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;\n"); + ok = append_list(lines, (long long)" ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {\n"); + ok = append_list(lines, (long long)" for (size_t i = 0; i < len; ++i) {\n"); + ok = append_list(lines, (long long)" ctx->data[ctx->datalen] = data[i];\n"); + ok = append_list(lines, (long long)" ctx->datalen++;\n"); + ok = append_list(lines, (long long)" if (ctx->datalen == 64) {\n"); + ok = append_list(lines, (long long)" ep_sha256_transform(ctx, ctx->data);\n"); + ok = append_list(lines, (long long)" ctx->bitlen += 512;\n"); + ok = append_list(lines, (long long)" ctx->datalen = 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {\n"); + ok = append_list(lines, (long long)" unsigned int i = ctx->datalen;\n"); + ok = append_list(lines, (long long)" if (ctx->datalen < 56) {\n"); + ok = append_list(lines, (long long)" ctx->data[i++] = 0x80;\n"); + ok = append_list(lines, (long long)" while (i < 56) ctx->data[i++] = 0x00;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" ctx->data[i++] = 0x80;\n"); + ok = append_list(lines, (long long)" while (i < 64) ctx->data[i++] = 0x00;\n"); + ok = append_list(lines, (long long)" ep_sha256_transform(ctx, ctx->data);\n"); + ok = append_list(lines, (long long)" memset(ctx->data, 0, 56);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ctx->bitlen += ctx->datalen * 8;\n"); + ok = append_list(lines, (long long)" ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;\n"); + ok = append_list(lines, (long long)" ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;\n"); + ok = append_list(lines, (long long)" ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;\n"); + ok = append_list(lines, (long long)" ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;\n"); + ok = append_list(lines, (long long)" ep_sha256_transform(ctx, ctx->data);\n"); + ok = append_list(lines, (long long)" for (i = 0; i < 4; ++i) {\n"); + ok = append_list(lines, (long long)" hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* ep_sha256(const char* s) {\n"); + ok = append_list(lines, (long long)" if (!s) s = \"\";\n"); + ok = append_list(lines, (long long)" EP_SHA256_CTX ctx;\n"); + ok = append_list(lines, (long long)" ep_sha256_init(&ctx);\n"); + ok = append_list(lines, (long long)" ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));\n"); + ok = append_list(lines, (long long)" unsigned char hash[32];\n"); + ok = append_list(lines, (long long)" ep_sha256_final(&ctx, hash);\n"); + ok = append_list(lines, (long long)" char* result = malloc(65);\n"); + ok = append_list(lines, (long long)" if (result) {\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 32; i++) {\n"); + ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" result[64] = '\\0';\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary\n"); + ok = append_list(lines, (long long)" safe), so keys/messages containing NUL bytes hash correctly. Returns a\n"); + ok = append_list(lines, (long long)" malloc'd 64-char lowercase hex string. */\n"); + ok = append_list(lines, (long long)"long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_20() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" const unsigned char* key = (const unsigned char*)key_ptr;\n"); + ok = append_list(lines, (long long)" const unsigned char* msg = (const unsigned char*)msg_ptr;\n"); + ok = append_list(lines, (long long)" size_t klen = (size_t)key_len;\n"); + ok = append_list(lines, (long long)" size_t mlen = (size_t)msg_len;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" unsigned char k0[64];\n"); + ok = append_list(lines, (long long)" memset(k0, 0, sizeof(k0));\n"); + ok = append_list(lines, (long long)" if (klen > 64) {\n"); + ok = append_list(lines, (long long)" /* Keys longer than the block size are replaced by their hash. */\n"); + ok = append_list(lines, (long long)" EP_SHA256_CTX kc;\n"); + ok = append_list(lines, (long long)" ep_sha256_init(&kc);\n"); + ok = append_list(lines, (long long)" ep_sha256_update(&kc, key ? key : (const unsigned char*)\"\", klen);\n"); + ok = append_list(lines, (long long)" unsigned char kh[32];\n"); + ok = append_list(lines, (long long)" ep_sha256_final(&kc, kh);\n"); + ok = append_list(lines, (long long)" memcpy(k0, kh, 32);\n"); + ok = append_list(lines, (long long)" } else if (key) {\n"); + ok = append_list(lines, (long long)" memcpy(k0, key, klen);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" unsigned char ipad[64], opad[64];\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 64; i++) {\n"); + ok = append_list(lines, (long long)" ipad[i] = k0[i] ^ 0x36;\n"); + ok = append_list(lines, (long long)" opad[i] = k0[i] ^ 0x5c;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" /* inner = H((K0 ^ ipad) || message) */\n"); + ok = append_list(lines, (long long)" EP_SHA256_CTX ic;\n"); + ok = append_list(lines, (long long)" ep_sha256_init(&ic);\n"); + ok = append_list(lines, (long long)" ep_sha256_update(&ic, ipad, 64);\n"); + ok = append_list(lines, (long long)" if (msg && mlen) ep_sha256_update(&ic, msg, mlen);\n"); + ok = append_list(lines, (long long)" unsigned char inner[32];\n"); + ok = append_list(lines, (long long)" ep_sha256_final(&ic, inner);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" /* mac = H((K0 ^ opad) || inner) */\n"); + ok = append_list(lines, (long long)" EP_SHA256_CTX oc;\n"); + ok = append_list(lines, (long long)" ep_sha256_init(&oc);\n"); + ok = append_list(lines, (long long)" ep_sha256_update(&oc, opad, 64);\n"); + ok = append_list(lines, (long long)" ep_sha256_update(&oc, inner, 32);\n"); + ok = append_list(lines, (long long)" unsigned char mac[32];\n"); + ok = append_list(lines, (long long)" ep_sha256_final(&oc, mac);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" char* out = (char*)malloc(65);\n"); + ok = append_list(lines, (long long)" if (out) {\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 32; i++) {\n"); + ok = append_list(lines, (long long)" snprintf(out + (i * 2), 3, \"%02x\", mac[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" out[64] = '\\0';\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return (long long)out;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" unsigned int count[2];\n"); + ok = append_list(lines, (long long)" unsigned int state[4];\n"); + ok = append_list(lines, (long long)" unsigned char buffer[64];\n"); + ok = append_list(lines, (long long)"} EP_MD5_CTX;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))\n"); + ok = append_list(lines, (long long)"#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))\n"); + ok = append_list(lines, (long long)"#define H(x,y,z) ((x) ^ (y) ^ (z))\n"); + ok = append_list(lines, (long long)"#define I(x,y,z) ((y) ^ ((x) | ~(z)))\n"); + ok = append_list(lines, (long long)"#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#define FF(a,b,c,d,x,s,ac) { \\\n"); + ok = append_list(lines, (long long)" (a) += F((b),(c),(d)) + (x) + (ac); \\\n"); + ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n"); + ok = append_list(lines, (long long)" (a) += (b); \\\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#define GG(a,b,c,d,x,s,ac) { \\\n"); + ok = append_list(lines, (long long)" (a) += G((b),(c),(d)) + (x) + (ac); \\\n"); + ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n"); + ok = append_list(lines, (long long)" (a) += (b); \\\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#define HH(a,b,c,d,x,s,ac) { \\\n"); + ok = append_list(lines, (long long)" (a) += H((b),(c),(d)) + (x) + (ac); \\\n"); + ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n"); + ok = append_list(lines, (long long)" (a) += (b); \\\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#define II(a,b,c,d,x,s,ac) { \\\n"); + ok = append_list(lines, (long long)" (a) += I((b),(c),(d)) + (x) + (ac); \\\n"); + ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n"); + ok = append_list(lines, (long long)" (a) += (b); \\\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_md5_init(EP_MD5_CTX *ctx) {\n"); + ok = append_list(lines, (long long)" ctx->count[0] = ctx->count[1] = 0;\n"); + ok = append_list(lines, (long long)" ctx->state[0] = 0x67452301;\n"); + ok = append_list(lines, (long long)" ctx->state[1] = 0xefcdab89;\n"); + ok = append_list(lines, (long long)" ctx->state[2] = 0x98badcfe;\n"); + ok = append_list(lines, (long long)" ctx->state[3] = 0x10325476;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {\n"); + ok = append_list(lines, (long long)" unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n"); + ok = append_list(lines, (long long)" for (int i = 0, j = 0; i < 16; i++, j += 4)\n"); + ok = append_list(lines, (long long)" x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);\n"); + ok = append_list(lines, (long long)" FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);\n"); + ok = append_list(lines, (long long)" FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);\n"); + ok = append_list(lines, (long long)" FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);\n"); + ok = append_list(lines, (long long)" GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);\n"); + ok = append_list(lines, (long long)" GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);\n"); + ok = append_list(lines, (long long)" GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);\n"); + ok = append_list(lines, (long long)" HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);\n"); + ok = append_list(lines, (long long)" HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);\n"); + ok = append_list(lines, (long long)" HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);\n"); + ok = append_list(lines, (long long)" II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);\n"); + ok = append_list(lines, (long long)" II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);\n"); + ok = append_list(lines, (long long)" II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" state[0] += a; state[1] += b; state[2] += c; state[3] += d;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {\n"); + ok = append_list(lines, (long long)" unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;\n"); + ok = append_list(lines, (long long)" ctx->count[0] += input_len << 3;\n"); + ok = append_list(lines, (long long)" if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;\n"); + ok = append_list(lines, (long long)" ctx->count[1] += input_len >> 29;\n"); + ok = append_list(lines, (long long)" if (input_len >= part_len) {\n"); + ok = append_list(lines, (long long)" memcpy(&ctx->buffer[index], input, part_len);\n"); + ok = append_list(lines, (long long)" ep_md5_transform(ctx->state, ctx->buffer);\n"); + ok = append_list(lines, (long long)" for (i = part_len; i + 63 < input_len; i += 64)\n"); + ok = append_list(lines, (long long)" ep_md5_transform(ctx->state, &input[i]);\n"); + ok = append_list(lines, (long long)" index = 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" memcpy(&ctx->buffer[index], &input[i], input_len - i);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n"); + ok = append_list(lines, (long long)" unsigned char bits[8];\n"); + ok = append_list(lines, (long long)" bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n"); + ok = append_list(lines, (long long)" bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n"); + ok = append_list(lines, (long long)" unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n"); + ok = append_list(lines, (long long)" unsigned char padding[64];\n"); + ok = append_list(lines, (long long)" memset(padding, 0, 64); padding[0] = 0x80;\n"); + ok = append_list(lines, (long long)" ep_md5_update(ctx, padding, pad_len);\n"); + ok = append_list(lines, (long long)" ep_md5_update(ctx, bits, 8);\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 4; i++) {\n"); + ok = append_list(lines, (long long)" digest[i*4] = ctx->state[i];\n"); + ok = append_list(lines, (long long)" digest[i*4 + 1] = ctx->state[i] >> 8;\n"); + ok = append_list(lines, (long long)" digest[i*4 + 2] = ctx->state[i] >> 16;\n"); + ok = append_list(lines, (long long)" digest[i*4 + 3] = ctx->state[i] >> 24;\n"); + ok = append_list(lines, (long long)" }\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_21() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* ep_md5(const char* s) {\n"); + ok = append_list(lines, (long long)" if (!s) s = \"\";\n"); + ok = append_list(lines, (long long)" EP_MD5_CTX ctx;\n"); + ok = append_list(lines, (long long)" ep_md5_init(&ctx);\n"); + ok = append_list(lines, (long long)" ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));\n"); + ok = append_list(lines, (long long)" unsigned char hash[16];\n"); + ok = append_list(lines, (long long)" ep_md5_final(&ctx, hash);\n"); + ok = append_list(lines, (long long)" char* result = malloc(33);\n"); + ok = append_list(lines, (long long)" if (result) {\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 16; i++) {\n"); + ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" result[32] = '\\0';\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* read_file_content(const char* filepath) {\n"); + ok = append_list(lines, (long long)" char mode[3];\n"); + ok = append_list(lines, (long long)" mode[0] = 'r';\n"); + ok = append_list(lines, (long long)" mode[1] = 'b';\n"); + ok = append_list(lines, (long long)" mode[2] = '\\0';\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(filepath, mode);\n"); + ok = append_list(lines, (long long)" if (!f) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" fseek(f, 0, SEEK_END);\n"); + ok = append_list(lines, (long long)" long size = ftell(f);\n"); + ok = append_list(lines, (long long)" fseek(f, 0, SEEK_SET);\n"); + ok = append_list(lines, (long long)" char* buf = malloc(size + 1);\n"); + ok = append_list(lines, (long long)" if (!buf) {\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" size_t read_bytes = fread(buf, 1, size, f);\n"); + ok = append_list(lines, (long long)" buf[read_bytes] = '\\0';\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_length(const char* s) {\n"); + ok = append_list(lines, (long long)" if (!s) return 0;\n"); + ok = append_list(lines, (long long)" return strlen(s);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long get_character(const char* s, long long index) {\n"); + ok = append_list(lines, (long long)" if (!s) return 0;\n"); + ok = append_list(lines, (long long)" long long len = strlen(s);\n"); + ok = append_list(lines, (long long)" if (index < 0 || index >= len) return 0;\n"); + ok = append_list(lines, (long long)" return (unsigned char)s[index];\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long create_list(void) {\n"); + ok = append_list(lines, (long long)" EpList* list = malloc(sizeof(EpList));\n"); + ok = append_list(lines, (long long)" if (!list) return 0;\n"); + ok = append_list(lines, (long long)" list->capacity = 4;\n"); + ok = append_list(lines, (long long)" list->length = 0;\n"); + ok = append_list(lines, (long long)" list->data = malloc(list->capacity * sizeof(long long));\n"); + ok = append_list(lines, (long long)" ep_gc_register(list, EP_OBJ_LIST);\n"); + ok = append_list(lines, (long long)" return (long long)list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long get_list_data_ptr(long long list_ptr) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (!list) return 0;\n"); + ok = append_list(lines, (long long)" return (long long)list->data;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long append_list(long long list_ptr, long long value) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (!list) return 0;\n"); + ok = append_list(lines, (long long)" if (list->length >= list->capacity) {\n"); + ok = append_list(lines, (long long)" list->capacity *= 2;\n"); + ok = append_list(lines, (long long)" list->data = realloc(list->data, list->capacity * sizeof(long long));\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" list->data[list->length] = value;\n"); + ok = append_list(lines, (long long)" list->length += 1;\n"); + ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)list_ptr, value);\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long get_list(long long list_ptr, long long index) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (index < 0 || index >= list->length) return 0;\n"); + ok = append_list(lines, (long long)" return list->data[index];\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long set_list(long long list_ptr, long long index, long long value) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (index < 0 || index >= list->length) return 0;\n"); + ok = append_list(lines, (long long)" list->data[index] = value;\n"); + ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)list_ptr, value);\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long length_list(long long list_ptr) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" return list->length;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long free_list(long long list_ptr) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (!list) return 0;\n"); + ok = append_list(lines, (long long)" /* Skip if already freed (idempotent) */\n"); + ok = append_list(lines, (long long)" if (!ep_gc_find(list)) return 0;\n"); + ok = append_list(lines, (long long)" ep_gc_unregister(list);\n"); + ok = append_list(lines, (long long)" free(list->data);\n"); + ok = append_list(lines, (long long)" free(list);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {\n"); + ok = append_list(lines, (long long)" EpList* rows = (EpList*)arg;\n"); + ok = append_list(lines, (long long)" EpList* row = (EpList*)create_list();\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < argc; i++) {\n"); + ok = append_list(lines, (long long)" char* val = argv[i] ? strdup(argv[i]) : strdup(\"\");\n"); + ok = append_list(lines, (long long)" append_list((long long)row, (long long)val);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" append_list((long long)rows, (long long)row);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long sqlite_get_callback_ptr(long long dummy) {\n"); + ok = append_list(lines, (long long)" return (long long)sqlite_list_callback;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* SQLite type-safe wrappers — marshal between int and long long */\n"); + ok = append_list(lines, (long long)"#ifdef EP_HAS_SQLITE\n"); + ok = append_list(lines, (long long)"typedef struct sqlite3 sqlite3;\n"); + ok = append_list(lines, (long long)"int sqlite3_open(const char*, sqlite3**);\n"); + ok = append_list(lines, (long long)"int sqlite3_close(sqlite3*);\n"); + ok = append_list(lines, (long long)"int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_open(long long filename, long long db_ptr) {\n"); + ok = append_list(lines, (long long)" sqlite3* db = NULL;\n"); + ok = append_list(lines, (long long)" int rc = sqlite3_open((const char*)filename, &db);\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_22() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" if (rc == 0 && db_ptr != 0) {\n"); + ok = append_list(lines, (long long)" *((long long*)db_ptr) = (long long)db;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return (long long)rc;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_close(long long db) {\n"); + ok = append_list(lines, (long long)" return (long long)sqlite3_close((sqlite3*)db);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) {\n"); + ok = append_list(lines, (long long)" return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql,\n"); + ok = append_list(lines, (long long)" (int(*)(void*,int,char**,char**))(callback),\n"); + ok = append_list(lines, (long long)" (void*)cb_arg, (char**)errmsg_ptr);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Prepared-statement API for parameterized queries (defeats SQL injection). */\n"); + ok = append_list(lines, (long long)"typedef struct sqlite3_stmt sqlite3_stmt;\n"); + ok = append_list(lines, (long long)"int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);\n"); + ok = append_list(lines, (long long)"int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));\n"); + ok = append_list(lines, (long long)"int sqlite3_bind_int64(sqlite3_stmt*, int, long long);\n"); + ok = append_list(lines, (long long)"int sqlite3_step(sqlite3_stmt*);\n"); + ok = append_list(lines, (long long)"int sqlite3_column_count(sqlite3_stmt*);\n"); + ok = append_list(lines, (long long)"const unsigned char* sqlite3_column_text(sqlite3_stmt*, int);\n"); + ok = append_list(lines, (long long)"long long sqlite3_column_int64(sqlite3_stmt*, int);\n"); + ok = append_list(lines, (long long)"int sqlite3_finalize(sqlite3_stmt*);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_prepare_v2(long long db, long long sql) {\n"); + ok = append_list(lines, (long long)" sqlite3_stmt* stmt = NULL;\n"); + ok = append_list(lines, (long long)" int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL);\n"); + ok = append_list(lines, (long long)" if (rc != 0) return 0;\n"); + ok = append_list(lines, (long long)" return (long long)stmt;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) {\n"); + ok = append_list(lines, (long long)" /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is\n"); + ok = append_list(lines, (long long)" a bound parameter, never concatenated into SQL — this is the safe path. */\n"); + ok = append_list(lines, (long long)" return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx,\n"); + ok = append_list(lines, (long long)" (const char*)value, -1, (void(*)(void*))(intptr_t)-1);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) {\n"); + ok = append_list(lines, (long long)" return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_step(long long stmt) {\n"); + ok = append_list(lines, (long long)" return (long long)sqlite3_step((sqlite3_stmt*)stmt);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_column_count(long long stmt) {\n"); + ok = append_list(lines, (long long)" return (long long)sqlite3_column_count((sqlite3_stmt*)stmt);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_column_text(long long stmt, long long col) {\n"); + ok = append_list(lines, (long long)" const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);\n"); + ok = append_list(lines, (long long)" if (!t) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" return (long long)strdup((const char*)t);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_column_int(long long stmt, long long col) {\n"); + ok = append_list(lines, (long long)" return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_finalize(long long stmt) {\n"); + ok = append_list(lines, (long long)" return (long long)sqlite3_finalize((sqlite3_stmt*)stmt);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif /* EP_HAS_SQLITE */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"int ep_argc = 0;\n"); + ok = append_list(lines, (long long)"char** ep_argv = NULL;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"void init_ep_args(int argc, char** argv) {\n"); + ok = append_list(lines, (long long)" ep_argc = argc;\n"); + ok = append_list(lines, (long long)" ep_argv = argv;\n"); + ok = append_list(lines, (long long)" ep_gc_register_thread((void*)&argc);\n"); + ok = append_list(lines, (long long)" /* Wire up channel scanning for GC (defined after EpChannel struct) */\n"); + ok = append_list(lines, (long long)" ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl;\n"); + ok = append_list(lines, (long long)" ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl;\n"); + ok = append_list(lines, (long long)" /* Wire up map value traversal for GC (defined after EpMap struct) */\n"); + ok = append_list(lines, (long long)" ep_gc_mark_map_values = ep_gc_mark_map_values_impl;\n"); + ok = append_list(lines, (long long)" ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long get_argument_count(void) {\n"); + ok = append_list(lines, (long long)" return ep_argc;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"const char* get_argument(long long index) {\n"); + ok = append_list(lines, (long long)" if (index < 0 || index >= ep_argc) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return ep_argv[index];\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long write_file_content(const char* filepath, const char* content) {\n"); + ok = append_list(lines, (long long)" char mode[3];\n"); + ok = append_list(lines, (long long)" mode[0] = 'w';\n"); + ok = append_list(lines, (long long)" mode[1] = 'b';\n"); + ok = append_list(lines, (long long)" mode[2] = '\\0';\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(filepath, mode);\n"); + ok = append_list(lines, (long long)" if (!f) return 0;\n"); + ok = append_list(lines, (long long)" size_t len = strlen(content);\n"); + ok = append_list(lines, (long long)" size_t written = fwrite(content, 1, len, f);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" return written == len ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long run_command(const char* command) {\n"); + ok = append_list(lines, (long long)" if (!command) return -1;\n"); + ok = append_list(lines, (long long)" return system(command);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* substring(const char* s, long long start, long long len) {\n"); + ok = append_list(lines, (long long)" if (!s) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" long long total_len = strlen(s);\n"); + ok = append_list(lines, (long long)" if (start < 0 || start >= total_len || len <= 0) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (start + len > total_len) {\n"); + ok = append_list(lines, (long long)" len = total_len - start;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" char* sub = malloc(len + 1);\n"); + ok = append_list(lines, (long long)" if (!sub) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" strncpy(sub, s + start, len);\n"); + ok = append_list(lines, (long long)" sub[len] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(sub, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return sub;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* string_from_list(long long list_ptr) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (!list) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_23() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" char* s = malloc(list->length + 1);\n"); + ok = append_list(lines, (long long)" if (!s) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n"); + ok = append_list(lines, (long long)" s[i] = (char)list->data[i];\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" s[list->length] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(s, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return s;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// Inverse of string_from_list: convert a string to a list of its byte values in\n"); + ok = append_list(lines, (long long)"// a single O(n) pass (one strlen + one copy). This lets callers iterate a string\n"); + ok = append_list(lines, (long long)"// in O(n) total via O(1) get_list, instead of O(n) get_character per index\n"); + ok = append_list(lines, (long long)"// (which is O(n^2) over the whole string).\n"); + ok = append_list(lines, (long long)"long long string_to_list(const char* s) {\n"); + ok = append_list(lines, (long long)" EpList* list = malloc(sizeof(EpList));\n"); + ok = append_list(lines, (long long)" if (!list) return 0;\n"); + ok = append_list(lines, (long long)" long long len = s ? (long long)strlen(s) : 0;\n"); + ok = append_list(lines, (long long)" list->capacity = len > 0 ? len : 4;\n"); + ok = append_list(lines, (long long)" list->length = len;\n"); + ok = append_list(lines, (long long)" list->data = malloc(list->capacity * sizeof(long long));\n"); + ok = append_list(lines, (long long)" if (!list->data) {\n"); + ok = append_list(lines, (long long)" list->capacity = 0;\n"); + ok = append_list(lines, (long long)" list->length = 0;\n"); + ok = append_list(lines, (long long)" ep_gc_register(list, EP_OBJ_LIST);\n"); + ok = append_list(lines, (long long)" return (long long)list;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) {\n"); + ok = append_list(lines, (long long)" list->data[i] = (unsigned char)s[i];\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_gc_register(list, EP_OBJ_LIST);\n"); + ok = append_list(lines, (long long)" return (long long)list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long pop_list(long long list_ptr) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (!list || list->length <= 0) return 0;\n"); + ok = append_list(lines, (long long)" list->length -= 1;\n"); + ok = append_list(lines, (long long)" return list->data[list->length];\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long remove_list(long long list_ptr, long long index) {\n"); + ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); + ok = append_list(lines, (long long)" if (!list || index < 0 || index >= list->length) return 0;\n"); + ok = append_list(lines, (long long)" long long removed = list->data[index];\n"); + ok = append_list(lines, (long long)" for (long long i = index; i < list->length - 1; i++) {\n"); + ok = append_list(lines, (long long)" list->data[i] = list->data[i + 1];\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" list->length -= 1;\n"); + ok = append_list(lines, (long long)" return removed;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long display_string(const char* s) {\n"); + ok = append_list(lines, (long long)" if (s) puts(s);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== File System Runtime ========== */\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #define mkdir(p, m) _mkdir(p)\n"); + ok = append_list(lines, (long long)" #define rmdir _rmdir\n"); + ok = append_list(lines, (long long)" #define getcwd _getcwd\n"); + ok = append_list(lines, (long long)" #define popen _popen\n"); + ok = append_list(lines, (long long)" #define pclose _pclose\n"); + ok = append_list(lines, (long long)" #define getpid _getpid\n"); + ok = append_list(lines, (long long)" #define setenv(k, v, o) _putenv_s(k, v)\n"); + ok = append_list(lines, (long long)" /* Minimal dirent polyfill for Windows */\n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" typedef struct { char d_name[260]; } ep_dirent;\n"); + ok = append_list(lines, (long long)" typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR;\n"); + ok = append_list(lines, (long long)" static EP_DIR* ep_opendir(const char* p) {\n"); + ok = append_list(lines, (long long)" EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR));\n"); + ok = append_list(lines, (long long)" char buf[270]; snprintf(buf, sizeof(buf), \"%s\\\\*\", p);\n"); + ok = append_list(lines, (long long)" d->hFind = FindFirstFileA(buf, &d->data);\n"); + ok = append_list(lines, (long long)" d->first = 1;\n"); + ok = append_list(lines, (long long)" return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" static ep_dirent* ep_readdir(EP_DIR* d) {\n"); + ok = append_list(lines, (long long)" static ep_dirent ent;\n"); + ok = append_list(lines, (long long)" if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; }\n"); + ok = append_list(lines, (long long)" if (!FindNextFileA(d->hFind, &d->data)) return NULL;\n"); + ok = append_list(lines, (long long)" strcpy(ent.d_name, d->data.cFileName); return &ent;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); }\n"); + ok = append_list(lines, (long long)" #define DIR EP_DIR\n"); + ok = append_list(lines, (long long)" #define dirent ep_dirent\n"); + ok = append_list(lines, (long long)" #define opendir ep_opendir\n"); + ok = append_list(lines, (long long)" #define readdir ep_readdir\n"); + ok = append_list(lines, (long long)" #define closedir ep_closedir\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)" #include \n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_read_file(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(path, \"rb\");\n"); + ok = append_list(lines, (long long)" if (!f) return (long long)\"\";\n"); + ok = append_list(lines, (long long)" fseek(f, 0, SEEK_END);\n"); + ok = append_list(lines, (long long)" long size = ftell(f);\n"); + ok = append_list(lines, (long long)" fseek(f, 0, SEEK_SET);\n"); + ok = append_list(lines, (long long)" char* buf = (char*)malloc(size + 1);\n"); + ok = append_list(lines, (long long)" fread(buf, 1, size, f);\n"); + ok = append_list(lines, (long long)" buf[size] = '\\0';\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_write_file(long long path_ptr, long long content_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" const char* content = (const char*)content_ptr;\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(path, \"wb\");\n"); + ok = append_list(lines, (long long)" if (!f) return 0;\n"); + ok = append_list(lines, (long long)" fputs(content, f);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_append_file(long long path_ptr, long long content_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" const char* content = (const char*)content_ptr;\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(path, \"ab\");\n"); + ok = append_list(lines, (long long)" if (!f) return 0;\n"); + ok = append_list(lines, (long long)" fputs(content, f);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_file_exists(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_is_directory(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_24() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" return S_ISDIR(st.st_mode) ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_file_size(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return -1;\n"); + ok = append_list(lines, (long long)" return (long long)st.st_size;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_list_directory(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" DIR* dir = opendir(path);\n"); + ok = append_list(lines, (long long)" if (!dir) return (long long)create_list();\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" struct dirent* entry;\n"); + ok = append_list(lines, (long long)" while ((entry = readdir(dir)) != NULL) {\n"); + ok = append_list(lines, (long long)" if (entry->d_name[0] == '.' && (entry->d_name[1] == '\\0' || \n"); + ok = append_list(lines, (long long)" (entry->d_name[1] == '.' && entry->d_name[2] == '\\0'))) continue;\n"); + ok = append_list(lines, (long long)" char* name = strdup(entry->d_name);\n"); + ok = append_list(lines, (long long)" append_list(list, (long long)name);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" closedir(dir);\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_create_directory(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" return mkdir(path, 0755) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_remove_file(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" return remove(path) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_remove_directory(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" return rmdir(path) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_rename_file(long long old_ptr, long long new_ptr) {\n"); + ok = append_list(lines, (long long)" return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_copy_file(long long src_ptr, long long dst_ptr) {\n"); + ok = append_list(lines, (long long)" const char* src = (const char*)src_ptr;\n"); + ok = append_list(lines, (long long)" const char* dst = (const char*)dst_ptr;\n"); + ok = append_list(lines, (long long)" FILE* fin = fopen(src, \"rb\");\n"); + ok = append_list(lines, (long long)" if (!fin) return 0;\n"); + ok = append_list(lines, (long long)" FILE* fout = fopen(dst, \"wb\");\n"); + ok = append_list(lines, (long long)" if (!fout) { fclose(fin); return 0; }\n"); + ok = append_list(lines, (long long)" char buf[8192];\n"); + ok = append_list(lines, (long long)" size_t n;\n"); + ok = append_list(lines, (long long)" while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {\n"); + ok = append_list(lines, (long long)" fwrite(buf, 1, n, fout);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" fclose(fin);\n"); + ok = append_list(lines, (long long)" fclose(fout);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Date/Time Runtime ========== */\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_now_ms(void) {\n"); + ok = append_list(lines, (long long)" struct timeval tv;\n"); + ok = append_list(lines, (long long)" gettimeofday(&tv, NULL);\n"); + ok = append_list(lines, (long long)" return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_now_sec(void) {\n"); + ok = append_list(lines, (long long)" return (long long)time(NULL);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_year(long long ts) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" return tm ? tm->tm_year + 1900 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_month(long long ts) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" return tm ? tm->tm_mon + 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_day(long long ts) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" return tm ? tm->tm_mday : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_hour(long long ts) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" return tm ? tm->tm_hour : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_minute(long long ts) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" return tm ? tm->tm_min : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_second(long long ts) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" return tm ? tm->tm_sec : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_time_weekday(long long ts) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" return tm ? tm->tm_wday : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_format_time(long long ts, long long fmt_ptr) {\n"); + ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n"); + ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n"); + ok = append_list(lines, (long long)" if (!tm) return (long long)\"\";\n"); + ok = append_list(lines, (long long)" char* buf = (char*)malloc(256);\n"); + ok = append_list(lines, (long long)" strftime(buf, 256, (const char*)fmt_ptr, tm);\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== OS Runtime ========== */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_getenv(long long name_ptr) {\n"); + ok = append_list(lines, (long long)" const char* val = getenv((const char*)name_ptr);\n"); + ok = append_list(lines, (long long)" return val ? (long long)val : (long long)\"\";\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_setenv(long long name_ptr, long long val_ptr) {\n"); + ok = append_list(lines, (long long)" return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_get_cwd(void) {\n"); + ok = append_list(lines, (long long)" char* buf = (char*)malloc(4096);\n"); + ok = append_list(lines, (long long)" if (getcwd(buf, 4096)) return (long long)buf;\n"); + ok = append_list(lines, (long long)" free(buf);\n"); + ok = append_list(lines, (long long)" return (long long)\"\";\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_os_name(void) {\n"); + ok = append_list(lines, (long long)" #if defined(__APPLE__)\n"); + ok = append_list(lines, (long long)" return (long long)\"macos\";\n"); + ok = append_list(lines, (long long)" #elif defined(__linux__)\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_25() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" return (long long)\"linux\";\n"); + ok = append_list(lines, (long long)" #elif defined(_WIN32)\n"); + ok = append_list(lines, (long long)" return (long long)\"windows\";\n"); + ok = append_list(lines, (long long)" #else\n"); + ok = append_list(lines, (long long)" return (long long)\"unknown\";\n"); + ok = append_list(lines, (long long)" #endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_arch_name(void) {\n"); + ok = append_list(lines, (long long)" #if defined(__aarch64__) || defined(__arm64__)\n"); + ok = append_list(lines, (long long)" return (long long)\"arm64\";\n"); + ok = append_list(lines, (long long)" #elif defined(__x86_64__)\n"); + ok = append_list(lines, (long long)" return (long long)\"x86_64\";\n"); + ok = append_list(lines, (long long)" #elif defined(__i386__)\n"); + ok = append_list(lines, (long long)" return (long long)\"x86\";\n"); + ok = append_list(lines, (long long)" #else\n"); + ok = append_list(lines, (long long)" return (long long)\"unknown\";\n"); + ok = append_list(lines, (long long)" #endif\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_exit(long long code) {\n"); + ok = append_list(lines, (long long)" exit((int)code);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_get_pid(void) {\n"); + ok = append_list(lines, (long long)" return (long long)getpid();\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_get_home_dir(void) {\n"); + ok = append_list(lines, (long long)" const char* home = getenv(\"HOME\");\n"); + ok = append_list(lines, (long long)" return home ? (long long)home : (long long)\"\";\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)"long long ep_run_command(long long cmd_ptr) {\n"); + ok = append_list(lines, (long long)" (void)cmd_ptr;\n"); + ok = append_list(lines, (long long)" return (long long)\"Error: running external commands is not supported on WebAssembly\";\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"long long ep_run_command(long long cmd_ptr) {\n"); + ok = append_list(lines, (long long)" const char* cmd = (const char*)cmd_ptr;\n"); + ok = append_list(lines, (long long)" FILE* fp = popen(cmd, \"r\");\n"); + ok = append_list(lines, (long long)" if (!fp) return (long long)\"\";\n"); + ok = append_list(lines, (long long)" char* result = (char*)malloc(65536);\n"); + ok = append_list(lines, (long long)" size_t total = 0;\n"); + ok = append_list(lines, (long long)" char buf[4096];\n"); + ok = append_list(lines, (long long)" while (fgets(buf, sizeof(buf), fp)) {\n"); + ok = append_list(lines, (long long)" size_t len = strlen(buf);\n"); + ok = append_list(lines, (long long)" memcpy(result + total, buf, len);\n"); + ok = append_list(lines, (long long)" total += len;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" result[total] = '\\0';\n"); + ok = append_list(lines, (long long)" pclose(fp);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== HashMap helpers ========== */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_hash_string(long long s_ptr) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_ptr;\n"); + ok = append_list(lines, (long long)" if (!s) return 0;\n"); + ok = append_list(lines, (long long)" unsigned long long hash = 5381;\n"); + ok = append_list(lines, (long long)" int c;\n"); + ok = append_list(lines, (long long)" while ((c = *s++)) {\n"); + ok = append_list(lines, (long long)" hash = ((hash << 5) + hash) + c;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return (long long)hash;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_str_equals(long long a_ptr, long long b_ptr) {\n"); + ok = append_list(lines, (long long)" if (a_ptr == b_ptr) return 1;\n"); + ok = append_list(lines, (long long)" if (!a_ptr || !b_ptr) return 0;\n"); + ok = append_list(lines, (long long)" /* If either value looks like a small integer (not a valid heap pointer),\n"); + ok = append_list(lines, (long long)" fall back to integer comparison — strcmp would segfault. */\n"); + ok = append_list(lines, (long long)" if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0;\n"); + ok = append_list(lines, (long long)" return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Sync Primitives ========== */\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)"long long ep_mutex_create(void) {\n"); + ok = append_list(lines, (long long)" CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));\n"); + ok = append_list(lines, (long long)" InitializeCriticalSection(m);\n"); + ok = append_list(lines, (long long)" return (long long)m;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_mutex_lock_fn(long long m) {\n"); + ok = append_list(lines, (long long)" EnterCriticalSection((CRITICAL_SECTION*)m);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_mutex_unlock_fn(long long m) {\n"); + ok = append_list(lines, (long long)" LeaveCriticalSection((CRITICAL_SECTION*)m);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_mutex_trylock(long long m) {\n"); + ok = append_list(lines, (long long)" return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_mutex_destroy(long long m) {\n"); + ok = append_list(lines, (long long)" DeleteCriticalSection((CRITICAL_SECTION*)m);\n"); + ok = append_list(lines, (long long)" free((void*)m);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"long long ep_mutex_create(void) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));\n"); + ok = append_list(lines, (long long)" pthread_mutex_init(m, NULL);\n"); + ok = append_list(lines, (long long)" return (long long)m;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_mutex_lock_fn(long long m) {\n"); + ok = append_list(lines, (long long)" return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_mutex_unlock_fn(long long m) {\n"); + ok = append_list(lines, (long long)" return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_mutex_trylock(long long m) {\n"); + ok = append_list(lines, (long long)" return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_mutex_destroy(long long m) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_destroy((pthread_mutex_t*)m);\n"); + ok = append_list(lines, (long long)" free((void*)m);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_create(void) {\n"); + ok = append_list(lines, (long long)" SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n"); + ok = append_list(lines, (long long)" InitializeSRWLock(rwl);\n"); + ok = append_list(lines, (long long)" return (long long)rwl;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n"); + ok = append_list(lines, (long long)" AcquireSRWLockShared((SRWLOCK*)rwl);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_write_lock(long long rwl) {\n"); + ok = append_list(lines, (long long)" AcquireSRWLockExclusive((SRWLOCK*)rwl);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_unlock(long long rwl) {\n"); + ok = append_list(lines, (long long)" /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n"); + ok = append_list(lines, (long long)" In practice the caller should know which lock was taken.\n"); + ok = append_list(lines, (long long)" ReleaseSRWLockExclusive on a shared lock is undefined, but\n"); + ok = append_list(lines, (long long)" the runtime guarantees matched lock/unlock pairs. We default\n"); + ok = append_list(lines, (long long)" to releasing the exclusive lock; shared unlock is handled\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_26() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" by pairing read_lock -> read_unlock if needed later. */\n"); + ok = append_list(lines, (long long)" ReleaseSRWLockExclusive((SRWLOCK*)rwl);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_destroy(long long rwl) {\n"); + ok = append_list(lines, (long long)" /* SRWLOCK has no destroy */\n"); + ok = append_list(lines, (long long)" free((void*)rwl);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_create(void) {\n"); + ok = append_list(lines, (long long)" pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));\n"); + ok = append_list(lines, (long long)" pthread_rwlock_init(rwl, NULL);\n"); + ok = append_list(lines, (long long)" return (long long)rwl;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n"); + ok = append_list(lines, (long long)" return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_write_lock(long long rwl) {\n"); + ok = append_list(lines, (long long)" return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_unlock(long long rwl) {\n"); + ok = append_list(lines, (long long)" return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_destroy(long long rwl) {\n"); + ok = append_list(lines, (long long)" pthread_rwlock_destroy((pthread_rwlock_t*)rwl);\n"); + ok = append_list(lines, (long long)" free((void*)rwl);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"#ifdef _MSC_VER\n"); + ok = append_list(lines, (long long)"long long ep_atomic_create(long long initial) {\n"); + ok = append_list(lines, (long long)" volatile long long* a = (volatile long long*)malloc(sizeof(long long));\n"); + ok = append_list(lines, (long long)" InterlockedExchange64(a, initial);\n"); + ok = append_list(lines, (long long)" return (long long)a;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_atomic_load(long long a) {\n"); + ok = append_list(lines, (long long)" return InterlockedCompareExchange64((volatile long long*)a, 0, 0);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_atomic_store(long long a, long long value) {\n"); + ok = append_list(lines, (long long)" InterlockedExchange64((volatile long long*)a, value);\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_atomic_add(long long a, long long delta) {\n"); + ok = append_list(lines, (long long)" return InterlockedExchangeAdd64((volatile long long*)a, delta);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_atomic_sub(long long a, long long delta) {\n"); + ok = append_list(lines, (long long)" return InterlockedExchangeAdd64((volatile long long*)a, -delta);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_atomic_cas(long long a, long long expected, long long desired) {\n"); + ok = append_list(lines, (long long)" long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected);\n"); + ok = append_list(lines, (long long)" return (old == expected) ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"long long ep_atomic_create(long long initial) {\n"); + ok = append_list(lines, (long long)" long long* a = (long long*)malloc(sizeof(long long));\n"); + ok = append_list(lines, (long long)" __atomic_store_n(a, initial, __ATOMIC_SEQ_CST);\n"); + ok = append_list(lines, (long long)" return (long long)a;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_atomic_load(long long a) {\n"); + ok = append_list(lines, (long long)" return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_atomic_store(long long a, long long value) {\n"); + ok = append_list(lines, (long long)" __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST);\n"); + ok = append_list(lines, (long long)" return value;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_atomic_add(long long a, long long delta) {\n"); + ok = append_list(lines, (long long)" return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_atomic_sub(long long a, long long delta) {\n"); + ok = append_list(lines, (long long)" return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_atomic_cas(long long a, long long expected, long long desired) {\n"); + ok = append_list(lines, (long long)" long long exp = expected;\n"); + ok = append_list(lines, (long long)" return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n"); + ok = append_list(lines, (long long)" pthread_cond_t cond;\n"); + ok = append_list(lines, (long long)" unsigned count;\n"); + ok = append_list(lines, (long long)" unsigned target;\n"); + ok = append_list(lines, (long long)" unsigned generation;\n"); + ok = append_list(lines, (long long)"} EpBarrier;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_barrier_create(long long count) {\n"); + ok = append_list(lines, (long long)" EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier));\n"); + ok = append_list(lines, (long long)" pthread_mutex_init(&b->mutex, NULL);\n"); + ok = append_list(lines, (long long)" pthread_cond_init(&b->cond, NULL);\n"); + ok = append_list(lines, (long long)" b->count = 0;\n"); + ok = append_list(lines, (long long)" b->target = (unsigned)count;\n"); + ok = append_list(lines, (long long)" b->generation = 0;\n"); + ok = append_list(lines, (long long)" return (long long)b;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_barrier_wait(long long bp) {\n"); + ok = append_list(lines, (long long)" EpBarrier* b = (EpBarrier*)bp;\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&b->mutex);\n"); + ok = append_list(lines, (long long)" unsigned gen = b->generation;\n"); + ok = append_list(lines, (long long)" b->count++;\n"); + ok = append_list(lines, (long long)" if (b->count >= b->target) {\n"); + ok = append_list(lines, (long long)" b->count = 0;\n"); + ok = append_list(lines, (long long)" b->generation++;\n"); + ok = append_list(lines, (long long)" pthread_cond_broadcast(&b->cond);\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&b->mutex);\n"); + ok = append_list(lines, (long long)" return 1; /* serial thread */\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" while (gen == b->generation) {\n"); + ok = append_list(lines, (long long)" pthread_cond_wait(&b->cond, &b->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&b->mutex);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_barrier_destroy(long long bp) {\n"); + ok = append_list(lines, (long long)" EpBarrier* b = (EpBarrier*)bp;\n"); + ok = append_list(lines, (long long)" pthread_mutex_destroy(&b->mutex);\n"); + ok = append_list(lines, (long long)" pthread_cond_destroy(&b->cond);\n"); + ok = append_list(lines, (long long)" free(b);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Semaphore via mutex+condvar (portable) */\n"); + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n"); + ok = append_list(lines, (long long)" pthread_cond_t cond;\n"); + ok = append_list(lines, (long long)" long long value;\n"); + ok = append_list(lines, (long long)"} EpSemaphore;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_semaphore_create(long long initial) {\n"); + ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));\n"); + ok = append_list(lines, (long long)" pthread_mutex_init(&s->mutex, NULL);\n"); + ok = append_list(lines, (long long)" pthread_cond_init(&s->cond, NULL);\n"); + ok = append_list(lines, (long long)" s->value = initial;\n"); + ok = append_list(lines, (long long)" return (long long)s;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_semaphore_wait(long long sp) {\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_27() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&s->mutex);\n"); + ok = append_list(lines, (long long)" while (s->value <= 0) {\n"); + ok = append_list(lines, (long long)" pthread_cond_wait(&s->cond, &s->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" s->value--;\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_semaphore_post(long long sp) {\n"); + ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&s->mutex);\n"); + ok = append_list(lines, (long long)" s->value++;\n"); + ok = append_list(lines, (long long)" pthread_cond_signal(&s->cond);\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_semaphore_trywait(long long sp) {\n"); + ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&s->mutex);\n"); + ok = append_list(lines, (long long)" if (s->value > 0) {\n"); + ok = append_list(lines, (long long)" s->value--;\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_semaphore_destroy(long long sp) {\n"); + ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n"); + ok = append_list(lines, (long long)" pthread_mutex_destroy(&s->mutex);\n"); + ok = append_list(lines, (long long)" pthread_cond_destroy(&s->cond);\n"); + ok = append_list(lines, (long long)" free(s);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_condvar_create(void) {\n"); + ok = append_list(lines, (long long)" pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));\n"); + ok = append_list(lines, (long long)" pthread_cond_init(cv, NULL);\n"); + ok = append_list(lines, (long long)" return (long long)cv;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_condvar_wait(long long cv, long long m) {\n"); + ok = append_list(lines, (long long)" return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_condvar_signal(long long cv) {\n"); + ok = append_list(lines, (long long)" return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_condvar_broadcast(long long cv) {\n"); + ok = append_list(lines, (long long)" return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_condvar_destroy(long long cv) {\n"); + ok = append_list(lines, (long long)" pthread_cond_destroy((pthread_cond_t*)cv);\n"); + ok = append_list(lines, (long long)" free((void*)cv);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Regex (simple stub — delegates to POSIX regex) ========== */\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_regex_match(long long pattern_ptr, long long text_ptr) {\n"); + ok = append_list(lines, (long long)" regex_t regex;\n"); + ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); + ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); + ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);\n"); + ok = append_list(lines, (long long)" if (ret) return 0;\n"); + ok = append_list(lines, (long long)" ret = regexec(®ex, text, 0, NULL, 0);\n"); + ok = append_list(lines, (long long)" regfree(®ex);\n"); + ok = append_list(lines, (long long)" return ret == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_regex_find(long long pattern_ptr, long long text_ptr) {\n"); + ok = append_list(lines, (long long)" regex_t regex;\n"); + ok = append_list(lines, (long long)" regmatch_t match;\n"); + ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); + ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); + ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n"); + ok = append_list(lines, (long long)" if (ret) return (long long)\"\";\n"); + ok = append_list(lines, (long long)" ret = regexec(®ex, text, 1, &match, 0);\n"); + ok = append_list(lines, (long long)" if (ret != 0) { regfree(®ex); return (long long)\"\"; }\n"); + ok = append_list(lines, (long long)" int len = match.rm_eo - match.rm_so;\n"); + ok = append_list(lines, (long long)" char* result = (char*)malloc(len + 1);\n"); + ok = append_list(lines, (long long)" memcpy(result, text + match.rm_so, len);\n"); + ok = append_list(lines, (long long)" result[len] = '\\0';\n"); + ok = append_list(lines, (long long)" regfree(®ex);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) {\n"); + ok = append_list(lines, (long long)" regex_t regex;\n"); + ok = append_list(lines, (long long)" regmatch_t match;\n"); + ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); + ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n"); + ok = append_list(lines, (long long)" if (ret) return list;\n"); + ok = append_list(lines, (long long)" const char* cursor = text;\n"); + ok = append_list(lines, (long long)" while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n"); + ok = append_list(lines, (long long)" int len = match.rm_eo - match.rm_so;\n"); + ok = append_list(lines, (long long)" char* result = (char*)malloc(len + 1);\n"); + ok = append_list(lines, (long long)" memcpy(result, cursor + match.rm_so, len);\n"); + ok = append_list(lines, (long long)" result[len] = '\\0';\n"); + ok = append_list(lines, (long long)" append_list(list, (long long)result);\n"); + ok = append_list(lines, (long long)" cursor += match.rm_eo;\n"); + ok = append_list(lines, (long long)" if (match.rm_eo == 0) break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" regfree(®ex);\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) {\n"); + ok = append_list(lines, (long long)" /* Simple single-replacement via regex */\n"); + ok = append_list(lines, (long long)" regex_t regex;\n"); + ok = append_list(lines, (long long)" regmatch_t match;\n"); + ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); + ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); + ok = append_list(lines, (long long)" const char* repl = (const char*)repl_ptr;\n"); + ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n"); + ok = append_list(lines, (long long)" if (ret) return text_ptr;\n"); + ok = append_list(lines, (long long)" ret = regexec(®ex, text, 1, &match, 0);\n"); + ok = append_list(lines, (long long)" if (ret != 0) { regfree(®ex); return text_ptr; }\n"); + ok = append_list(lines, (long long)" size_t tlen = strlen(text);\n"); + ok = append_list(lines, (long long)" size_t rlen = strlen(repl);\n"); + ok = append_list(lines, (long long)" size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen;\n"); + ok = append_list(lines, (long long)" char* result = (char*)malloc(new_len + 1);\n"); + ok = append_list(lines, (long long)" memcpy(result, text, match.rm_so);\n"); + ok = append_list(lines, (long long)" memcpy(result + match.rm_so, repl, rlen);\n"); + ok = append_list(lines, (long long)" memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\n"); + ok = append_list(lines, (long long)" result[new_len] = '\\0';\n"); + ok = append_list(lines, (long long)" regfree(®ex);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_regex_split(long long pattern_ptr, long long text_ptr) {\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" /* Simple split: find matches and split around them */\n"); + ok = append_list(lines, (long long)" regex_t regex;\n"); + ok = append_list(lines, (long long)" regmatch_t match;\n"); + ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); + ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); + ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n"); + ok = append_list(lines, (long long)" if (ret) {\n"); + ok = append_list(lines, (long long)" append_list(list, text_ptr);\n"); + ok = append_list(lines, (long long)" return list;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_28() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" const char* cursor = text;\n"); + ok = append_list(lines, (long long)" while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n"); + ok = append_list(lines, (long long)" int len = match.rm_so;\n"); + ok = append_list(lines, (long long)" char* part = (char*)malloc(len + 1);\n"); + ok = append_list(lines, (long long)" memcpy(part, cursor, len);\n"); + ok = append_list(lines, (long long)" part[len] = '\\0';\n"); + ok = append_list(lines, (long long)" append_list(list, (long long)part);\n"); + ok = append_list(lines, (long long)" cursor += match.rm_eo;\n"); + ok = append_list(lines, (long long)" if (match.rm_eo == 0) break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" char* rest = strdup(cursor);\n"); + ok = append_list(lines, (long long)" append_list(list, (long long)rest);\n"); + ok = append_list(lines, (long long)" regfree(®ex);\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Base64 ========== */\n"); + ok = append_list(lines, (long long)"static const char b64_table[] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_base64_encode(long long data_ptr) {\n"); + ok = append_list(lines, (long long)" const unsigned char* data = (const unsigned char*)data_ptr;\n"); + ok = append_list(lines, (long long)" size_t len = strlen((const char*)data);\n"); + ok = append_list(lines, (long long)" size_t out_len = 4 * ((len + 2) / 3);\n"); + ok = append_list(lines, (long long)" char* out = (char*)malloc(out_len + 1);\n"); + ok = append_list(lines, (long long)" size_t i, j = 0;\n"); + ok = append_list(lines, (long long)" for (i = 0; i < len; i += 3) {\n"); + ok = append_list(lines, (long long)" unsigned int n = data[i] << 16;\n"); + ok = append_list(lines, (long long)" if (i + 1 < len) n |= data[i+1] << 8;\n"); + ok = append_list(lines, (long long)" if (i + 2 < len) n |= data[i+2];\n"); + ok = append_list(lines, (long long)" out[j++] = b64_table[(n >> 18) & 63];\n"); + ok = append_list(lines, (long long)" out[j++] = b64_table[(n >> 12) & 63];\n"); + ok = append_list(lines, (long long)" out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '=';\n"); + ok = append_list(lines, (long long)" out[j++] = (i + 2 < len) ? b64_table[n & 63] : '=';\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" out[j] = '\\0';\n"); + ok = append_list(lines, (long long)" return (long long)out;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_uuid_v4(void) {\n"); + ok = append_list(lines, (long long)" char* uuid = (char*)malloc(37);\n"); + ok = append_list(lines, (long long)" unsigned char bytes[16];\n"); + ok = append_list(lines, (long long)" ep_secure_random_bytes(bytes, 16);\n"); + ok = append_list(lines, (long long)" bytes[6] = (bytes[6] & 0x0F) | 0x40;\n"); + ok = append_list(lines, (long long)" bytes[8] = (bytes[8] & 0x3F) | 0x80;\n"); + ok = append_list(lines, (long long)" snprintf(uuid, 37, \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n"); + ok = append_list(lines, (long long)" bytes[0], bytes[1], bytes[2], bytes[3],\n"); + ok = append_list(lines, (long long)" bytes[4], bytes[5], bytes[6], bytes[7],\n"); + ok = append_list(lines, (long long)" bytes[8], bytes[9], bytes[10], bytes[11],\n"); + ok = append_list(lines, (long long)" bytes[12], bytes[13], bytes[14], bytes[15]);\n"); + ok = append_list(lines, (long long)" return (long long)uuid;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long file_read(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" if (!path) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(path, \"rb\");\n"); + ok = append_list(lines, (long long)" if (!f) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" fseek(f, 0, SEEK_END);\n"); + ok = append_list(lines, (long long)" long size = ftell(f);\n"); + ok = append_list(lines, (long long)" fseek(f, 0, SEEK_SET);\n"); + ok = append_list(lines, (long long)" char* buf = malloc(size + 1);\n"); + ok = append_list(lines, (long long)" if (!buf) { fclose(f); return (long long)strdup(\"\"); }\n"); + ok = append_list(lines, (long long)" fread(buf, 1, size, f);\n"); + ok = append_list(lines, (long long)" buf[size] = '\\0';\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long file_write(long long path_val, long long content_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" const char* content = (const char*)content_val;\n"); + ok = append_list(lines, (long long)" if (!path || !content) return 0;\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(path, \"wb\");\n"); + ok = append_list(lines, (long long)" if (!f) return 0;\n"); + ok = append_list(lines, (long long)" size_t len = strlen(content);\n"); + ok = append_list(lines, (long long)" fwrite(content, 1, len, f);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long file_append(long long path_val, long long content_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" const char* content = (const char*)content_val;\n"); + ok = append_list(lines, (long long)" if (!path || !content) return 0;\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(path, \"ab\");\n"); + ok = append_list(lines, (long long)" if (!f) return 0;\n"); + ok = append_list(lines, (long long)" size_t len = strlen(content);\n"); + ok = append_list(lines, (long long)" fwrite(content, 1, len, f);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long file_exists(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" if (!path) return 0;\n"); + ok = append_list(lines, (long long)" FILE* f = fopen(path, \"r\");\n"); + ok = append_list(lines, (long long)" if (f) { fclose(f); return 1; }\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_contains(long long s_val, long long sub_val) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" const char* sub = (const char*)sub_val;\n"); + ok = append_list(lines, (long long)" if (!s || !sub) return 0;\n"); + ok = append_list(lines, (long long)" return strstr(s, sub) != NULL ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_index_of(long long s_val, long long sub_val) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" const char* sub = (const char*)sub_val;\n"); + ok = append_list(lines, (long long)" if (!s || !sub) return -1;\n"); + ok = append_list(lines, (long long)" const char* found = strstr(s, sub);\n"); + ok = append_list(lines, (long long)" if (!found) return -1;\n"); + ok = append_list(lines, (long long)" return (long long)(found - s);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_replace(long long s_val, long long old_val, long long new_val) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" const char* old_str = (const char*)old_val;\n"); + ok = append_list(lines, (long long)" const char* new_str = (const char*)new_val;\n"); + ok = append_list(lines, (long long)" if (!s || !old_str || !new_str) return (long long)strdup(s ? s : \"\");\n"); + ok = append_list(lines, (long long)" size_t old_len = strlen(old_str);\n"); + ok = append_list(lines, (long long)" size_t new_len = strlen(new_str);\n"); + ok = append_list(lines, (long long)" if (old_len == 0) return (long long)strdup(s);\n"); + ok = append_list(lines, (long long)" int count = 0;\n"); + ok = append_list(lines, (long long)" const char* p = s;\n"); + ok = append_list(lines, (long long)" while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; }\n"); + ok = append_list(lines, (long long)" size_t result_len = strlen(s) + count * (new_len - old_len);\n"); + ok = append_list(lines, (long long)" char* result = malloc(result_len + 1);\n"); + ok = append_list(lines, (long long)" if (!result) return (long long)strdup(s);\n"); + ok = append_list(lines, (long long)" char* dst = result;\n"); + ok = append_list(lines, (long long)" p = s;\n"); + ok = append_list(lines, (long long)" while (*p) {\n"); + ok = append_list(lines, (long long)" if (strncmp(p, old_str, old_len) == 0) {\n"); + ok = append_list(lines, (long long)" memcpy(dst, new_str, new_len);\n"); + ok = append_list(lines, (long long)" dst += new_len;\n"); + ok = append_list(lines, (long long)" p += old_len;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" *dst++ = *p++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" *dst = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Additional String Functions ========== */\n"); + ok = append_list(lines, (long long)"#include \n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_29() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_upper(long long s_val) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" if (!s) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" long long len = strlen(s);\n"); + ok = append_list(lines, (long long)" char* result = malloc(len + 1);\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]);\n"); + ok = append_list(lines, (long long)" result[len] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_lower(long long s_val) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" if (!s) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" long long len = strlen(s);\n"); + ok = append_list(lines, (long long)" char* result = malloc(len + 1);\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]);\n"); + ok = append_list(lines, (long long)" result[len] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_trim(long long s_val) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" if (!s) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" while (*s && isspace((unsigned char)*s)) s++;\n"); + ok = append_list(lines, (long long)" long long len = strlen(s);\n"); + ok = append_list(lines, (long long)" while (len > 0 && isspace((unsigned char)s[len - 1])) len--;\n"); + ok = append_list(lines, (long long)" char* result = malloc(len + 1);\n"); + ok = append_list(lines, (long long)" memcpy(result, s, len);\n"); + ok = append_list(lines, (long long)" result[len] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_split(long long s_val, long long delim_val) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" const char* delim = (const char*)delim_val;\n"); + ok = append_list(lines, (long long)" if (!s || !delim) return create_list();\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" long long dlen = strlen(delim);\n"); + ok = append_list(lines, (long long)" if (dlen == 0) { append_list(list, s_val); return list; }\n"); + ok = append_list(lines, (long long)" const char* p = s;\n"); + ok = append_list(lines, (long long)" while (1) {\n"); + ok = append_list(lines, (long long)" const char* found = strstr(p, delim);\n"); + ok = append_list(lines, (long long)" long long partlen = found ? (found - p) : (long long)strlen(p);\n"); + ok = append_list(lines, (long long)" char* part = malloc(partlen + 1);\n"); + ok = append_list(lines, (long long)" memcpy(part, p, partlen);\n"); + ok = append_list(lines, (long long)" part[partlen] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(part, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" append_list(list, (long long)part);\n"); + ok = append_list(lines, (long long)" if (!found) break;\n"); + ok = append_list(lines, (long long)" p = found + dlen;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long char_at(long long s_val, long long index) {\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); + ok = append_list(lines, (long long)" if (!s || index < 0 || index >= (long long)strlen(s)) return 0;\n"); + ok = append_list(lines, (long long)" return (unsigned char)s[index];\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long char_from_code(long long code) {\n"); + ok = append_list(lines, (long long)" char* result = malloc(2);\n"); + ok = append_list(lines, (long long)" result[0] = (char)code;\n"); + ok = append_list(lines, (long long)" result[1] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_abs(long long n) {\n"); + ok = append_list(lines, (long long)" return n < 0 ? -n : n;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// Auto-convert any value to string for string interpolation\n"); + ok = append_list(lines, (long long)"long long ep_auto_to_string(long long val) {\n"); + ok = append_list(lines, (long long)" // If the value is 0, return \"0\"\n"); + ok = append_list(lines, (long long)" if (val == 0) return (long long)strdup(\"0\");\n"); + ok = append_list(lines, (long long)" // Check if val is a GC-tracked string (heap-allocated)\n"); + ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_find((void*)val);\n"); + ok = append_list(lines, (long long)" if (obj && obj->kind == EP_OBJ_STRING) {\n"); + ok = append_list(lines, (long long)" return val; // It's a known string pointer\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" // Check if val is a static string literal (in .rodata/.data segment)\n"); + ok = append_list(lines, (long long)" // These aren't GC-tracked but ARE valid pointers. Use a safe probe:\n"); + ok = append_list(lines, (long long)" // only dereference if the address is in a readable memory page.\n"); + ok = append_list(lines, (long long)" if (val > 0x100000) {\n"); + ok = append_list(lines, (long long)"#if defined(_WIN32)\n"); + ok = append_list(lines, (long long)" // Windows: use VirtualQuery to safely probe pointer validity\n"); + ok = append_list(lines, (long long)" MEMORY_BASIC_INFORMATION mbi;\n"); + ok = append_list(lines, (long long)" if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) {\n"); + ok = append_list(lines, (long long)" const char* p = (const char*)(void*)val;\n"); + ok = append_list(lines, (long long)" unsigned char first = (unsigned char)*p;\n"); + ok = append_list(lines, (long long)" if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n"); + ok = append_list(lines, (long long)" return val; // Readable memory, looks like a string\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#elif defined(__APPLE__)\n"); + ok = append_list(lines, (long long)" // macOS: use vm_read_overwrite to safely probe\n"); + ok = append_list(lines, (long long)" char probe;\n"); + ok = append_list(lines, (long long)" vm_size_t sz = 1;\n"); + ok = append_list(lines, (long long)" kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz);\n"); + ok = append_list(lines, (long long)" if (kr == KERN_SUCCESS) {\n"); + ok = append_list(lines, (long long)" unsigned char first = (unsigned char)probe;\n"); + ok = append_list(lines, (long long)" if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n"); + ok = append_list(lines, (long long)" return val; // Readable memory, looks like a string\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" // Linux: use write() to /dev/null as a safe pointer probe\n"); + ok = append_list(lines, (long long)" // write() returns -1 with EFAULT for invalid pointers, no signal\n"); + ok = append_list(lines, (long long)" int devnull = open(\"/dev/null\", 1); // O_WRONLY\n"); + ok = append_list(lines, (long long)" if (devnull >= 0) {\n"); + ok = append_list(lines, (long long)" ssize_t r = write(devnull, (const void*)val, 1);\n"); + ok = append_list(lines, (long long)" close(devnull);\n"); + ok = append_list(lines, (long long)" if (r == 1) {\n"); + ok = append_list(lines, (long long)" const char* p = (const char*)(void*)val;\n"); + ok = append_list(lines, (long long)" unsigned char first = (unsigned char)*p;\n"); + ok = append_list(lines, (long long)" if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n"); + ok = append_list(lines, (long long)" return val;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" // Otherwise, convert integer to string\n"); + ok = append_list(lines, (long long)" char* buf = (char*)malloc(32);\n"); + ok = append_list(lines, (long long)" snprintf(buf, 32, \"%lld\", val);\n"); + ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_random_int(long long min, long long max) {\n"); + ok = append_list(lines, (long long)" if (max <= min) return min;\n"); + ok = append_list(lines, (long long)" /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n"); + ok = append_list(lines, (long long)" unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n"); + ok = append_list(lines, (long long)" unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);\n"); + ok = append_list(lines, (long long)" unsigned long long r;\n"); + ok = append_list(lines, (long long)" do {\n"); + ok = append_list(lines, (long long)" ep_secure_random_bytes((unsigned char*)&r, sizeof(r));\n"); + ok = append_list(lines, (long long)" } while (r >= limit);\n"); + ok = append_list(lines, (long long)" return min + (long long)(r % range);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// JSON built-in functions\n"); + ok = append_list(lines, (long long)"static const char* json_skip_ws(const char* p) {\n"); + ok = append_list(lines, (long long)" while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n"); + ok = append_list(lines, (long long)" return p;\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_30() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static const char* json_skip_value(const char* p) {\n"); + ok = append_list(lines, (long long)" p = json_skip_ws(p);\n"); + ok = append_list(lines, (long long)" if (*p == '\"') {\n"); + ok = append_list(lines, (long long)" p++;\n"); + ok = append_list(lines, (long long)" while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n"); + ok = append_list(lines, (long long)" if (*p == '\"') p++;\n"); + ok = append_list(lines, (long long)" } else if (*p == '{') {\n"); + ok = append_list(lines, (long long)" int depth = 1; p++;\n"); + ok = append_list(lines, (long long)" while (*p && depth > 0) {\n"); + ok = append_list(lines, (long long)" if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n"); + ok = append_list(lines, (long long)" else if (*p == '{') { depth++; p++; }\n"); + ok = append_list(lines, (long long)" else if (*p == '}') { depth--; p++; }\n"); + ok = append_list(lines, (long long)" else p++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else if (*p == '[') {\n"); + ok = append_list(lines, (long long)" int depth = 1; p++;\n"); + ok = append_list(lines, (long long)" while (*p && depth > 0) {\n"); + ok = append_list(lines, (long long)" if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n"); + ok = append_list(lines, (long long)" else if (*p == '[') { depth++; p++; }\n"); + ok = append_list(lines, (long long)" else if (*p == ']') { depth--; p++; }\n"); + ok = append_list(lines, (long long)" else p++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\\n') p++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return p;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static const char* json_find_key(const char* json, const char* key) {\n"); + ok = append_list(lines, (long long)" const char* p = json_skip_ws(json);\n"); + ok = append_list(lines, (long long)" if (*p != '{') return NULL;\n"); + ok = append_list(lines, (long long)" p++;\n"); + ok = append_list(lines, (long long)" while (*p) {\n"); + ok = append_list(lines, (long long)" p = json_skip_ws(p);\n"); + ok = append_list(lines, (long long)" if (*p == '}') return NULL;\n"); + ok = append_list(lines, (long long)" if (*p != '\"') return NULL;\n"); + ok = append_list(lines, (long long)" p++;\n"); + ok = append_list(lines, (long long)" const char* ks = p;\n"); + ok = append_list(lines, (long long)" while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n"); + ok = append_list(lines, (long long)" size_t klen = p - ks;\n"); + ok = append_list(lines, (long long)" if (*p == '\"') p++;\n"); + ok = append_list(lines, (long long)" p = json_skip_ws(p);\n"); + ok = append_list(lines, (long long)" if (*p == ':') p++;\n"); + ok = append_list(lines, (long long)" p = json_skip_ws(p);\n"); + ok = append_list(lines, (long long)" if (klen == strlen(key) && strncmp(ks, key, klen) == 0) {\n"); + ok = append_list(lines, (long long)" return p;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" p = json_skip_value(p);\n"); + ok = append_list(lines, (long long)" p = json_skip_ws(p);\n"); + ok = append_list(lines, (long long)" if (*p == ',') p++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return NULL;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long json_get_string(long long json_val, long long key_val) {\n"); + ok = append_list(lines, (long long)" const char* json = (const char*)json_val;\n"); + ok = append_list(lines, (long long)" const char* key = (const char*)key_val;\n"); + ok = append_list(lines, (long long)" if (!json || !key) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" const char* val = json_find_key(json, key);\n"); + ok = append_list(lines, (long long)" if (!val || *val != '\"') return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" val++;\n"); + ok = append_list(lines, (long long)" const char* end = val;\n"); + ok = append_list(lines, (long long)" while (*end && *end != '\"') { if (*end == '\\\\') end++; end++; }\n"); + ok = append_list(lines, (long long)" size_t len = end - val;\n"); + ok = append_list(lines, (long long)" char* result = (char*)malloc(len + 1);\n"); + ok = append_list(lines, (long long)" // Handle escape sequences\n"); + ok = append_list(lines, (long long)" size_t di = 0;\n"); + ok = append_list(lines, (long long)" const char* si = val;\n"); + ok = append_list(lines, (long long)" while (si < end) {\n"); + ok = append_list(lines, (long long)" if (*si == '\\\\' && si + 1 < end) {\n"); + ok = append_list(lines, (long long)" si++;\n"); + ok = append_list(lines, (long long)" switch (*si) {\n"); + ok = append_list(lines, (long long)" case 'n': result[di++] = '\\n'; break;\n"); + ok = append_list(lines, (long long)" case 't': result[di++] = '\\t'; break;\n"); + ok = append_list(lines, (long long)" case 'r': result[di++] = '\\r'; break;\n"); + ok = append_list(lines, (long long)" case '\"': result[di++] = '\"'; break;\n"); + ok = append_list(lines, (long long)" case '\\\\': result[di++] = '\\\\'; break;\n"); + ok = append_list(lines, (long long)" default: result[di++] = *si; break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" result[di++] = *si;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" si++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" result[di] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long json_get_int(long long json_val, long long key_val) {\n"); + ok = append_list(lines, (long long)" const char* json = (const char*)json_val;\n"); + ok = append_list(lines, (long long)" const char* key = (const char*)key_val;\n"); + ok = append_list(lines, (long long)" if (!json || !key) return 0;\n"); + ok = append_list(lines, (long long)" const char* val = json_find_key(json, key);\n"); + ok = append_list(lines, (long long)" if (!val) return 0;\n"); + ok = append_list(lines, (long long)" return atoll(val);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long json_get_bool(long long json_val, long long key_val) {\n"); + ok = append_list(lines, (long long)" const char* json = (const char*)json_val;\n"); + ok = append_list(lines, (long long)" const char* key = (const char*)key_val;\n"); + ok = append_list(lines, (long long)" if (!json || !key) return 0;\n"); + ok = append_list(lines, (long long)" const char* val = json_find_key(json, key);\n"); + ok = append_list(lines, (long long)" if (!val) return 0;\n"); + ok = append_list(lines, (long long)" if (strncmp(val, \"true\", 4) == 0) return 1;\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// SHA-1 implementation (RFC 3174) for WebSocket handshake\n"); + ok = append_list(lines, (long long)"static unsigned int sha1_left_rotate(unsigned int x, int n) {\n"); + ok = append_list(lines, (long long)" return (x << n) | (x >> (32 - n));\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sha1(long long data_val) {\n"); + ok = append_list(lines, (long long)" const unsigned char* data = (const unsigned char*)data_val;\n"); + ok = append_list(lines, (long long)" if (!data) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" size_t len = strlen((const char*)data);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;\n"); + ok = append_list(lines, (long long)" size_t new_len = len + 1;\n"); + ok = append_list(lines, (long long)" while (new_len % 64 != 56) new_len++;\n"); + ok = append_list(lines, (long long)" unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);\n"); + ok = append_list(lines, (long long)" memcpy(msg, data, len);\n"); + ok = append_list(lines, (long long)" msg[len] = 0x80;\n"); + ok = append_list(lines, (long long)" unsigned long long bits_len = (unsigned long long)len * 8;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" for (size_t offset = 0; offset < new_len + 8; offset += 64) {\n"); + ok = append_list(lines, (long long)" unsigned int w[80];\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 16; i++) {\n"); + ok = append_list(lines, (long long)" w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n"); + ok = append_list(lines, (long long)" ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n"); + ok = append_list(lines, (long long)" unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 80; i++) {\n"); + ok = append_list(lines, (long long)" unsigned int f, k;\n"); + ok = append_list(lines, (long long)" if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }\n"); + ok = append_list(lines, (long long)" else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }\n"); + ok = append_list(lines, (long long)" else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }\n"); + ok = append_list(lines, (long long)" else { f = b ^ c ^ d; k = 0xCA62C1D6; }\n"); + ok = append_list(lines, (long long)" unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n"); + ok = append_list(lines, (long long)" e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(msg);\n"); + ok = append_list(lines, (long long)"\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_core_31() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)" // Return Base64-encoded hash directly (for WebSocket handshake)\n"); + ok = append_list(lines, (long long)" unsigned char hash[20];\n"); + ok = append_list(lines, (long long)" hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;\n"); + ok = append_list(lines, (long long)" hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF;\n"); + ok = append_list(lines, (long long)" hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF;\n"); + ok = append_list(lines, (long long)" hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF;\n"); + ok = append_list(lines, (long long)" hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" // Base64 encode the 20-byte hash\n"); + ok = append_list(lines, (long long)" size_t b64_len = 4 * ((20 + 2) / 3);\n"); + ok = append_list(lines, (long long)" char* result = (char*)malloc(b64_len + 1);\n"); + ok = append_list(lines, (long long)" size_t j = 0;\n"); + ok = append_list(lines, (long long)" for (size_t bi = 0; bi < 20; bi += 3) {\n"); + ok = append_list(lines, (long long)" unsigned int n2 = ((unsigned int)hash[bi]) << 16;\n"); + ok = append_list(lines, (long long)" if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8;\n"); + ok = append_list(lines, (long long)" if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2];\n"); + ok = append_list(lines, (long long)" result[j++] = b64_table[(n2 >> 18) & 0x3F];\n"); + ok = append_list(lines, (long long)" result[j++] = b64_table[(n2 >> 12) & 0x3F];\n"); + ok = append_list(lines, (long long)" result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '=';\n"); + ok = append_list(lines, (long long)" result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '=';\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" result[j] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"// Read exact N bytes from a socket\n"); + ok = append_list(lines, (long long)"#ifdef __wasm__\n"); + ok = append_list(lines, (long long)"long long ep_net_recv_bytes(long long fd, long long count) {\n"); + ok = append_list(lines, (long long)" (void)fd; (void)count;\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)"long long ep_net_recv_bytes(long long fd, long long count) {\n"); + ok = append_list(lines, (long long)" if (count <= 0) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" char* buf = (char*)malloc(count + 1);\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" int total = 0;\n"); + ok = append_list(lines, (long long)" while (total < (int)count) {\n"); + ok = append_list(lines, (long long)" int n = recv((int)fd, buf + total, (int)(count - total), 0);\n"); + ok = append_list(lines, (long long)" if (n <= 0) break;\n"); + ok = append_list(lines, (long long)" total += n;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" ssize_t total = 0;\n"); + ok = append_list(lines, (long long)" while (total < count) {\n"); + ok = append_list(lines, (long long)" ssize_t n = recv((int)fd, buf + total, count - total, 0);\n"); + ok = append_list(lines, (long long)" if (n <= 0) break;\n"); + ok = append_list(lines, (long long)" total += n;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" buf[total] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_get_args(void) {\n"); + ok = append_list(lines, (long long)" long long list_ptr = create_list();\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < ep_argc; i++) {\n"); + ok = append_list(lines, (long long)" char* arg_copy = strdup(ep_argv[i]);\n"); + ok = append_list(lines, (long long)" ep_gc_register(arg_copy, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" append_list(list_ptr, (long long)arg_copy);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return list_ptr;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_builtins_0() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Built-in: string concatenation */\n"); + ok = append_list(lines, (long long)"long long concat(long long a, long long b) {\n"); + ok = append_list(lines, (long long)" const char* sa = (const char*)a;\n"); + ok = append_list(lines, (long long)" const char* sb = (const char*)b;\n"); + ok = append_list(lines, (long long)" long long la = strlen(sa);\n"); + ok = append_list(lines, (long long)" long long lb = strlen(sb);\n"); + ok = append_list(lines, (long long)" char* result = malloc(la + lb + 1);\n"); + ok = append_list(lines, (long long)" memcpy(result, sa, la);\n"); + ok = append_list(lines, (long long)" memcpy(result + la, sb, lb);\n"); + ok = append_list(lines, (long long)" result[la + lb] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long int_to_string(long long val) {\n"); + ok = append_list(lines, (long long)" char* buf = malloc(32);\n"); + ok = append_list(lines, (long long)" snprintf(buf, 32, \"%lld\", val);\n"); + ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_int_to_str(long long val) { return int_to_string(val); }\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"typedef struct { char* data; long long len; long long cap; } EpStringBuilder;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sb_create(long long dummy) {\n"); + ok = append_list(lines, (long long)" (void)dummy;\n"); + ok = append_list(lines, (long long)" EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));\n"); + ok = append_list(lines, (long long)" sb->cap = 256;\n"); + ok = append_list(lines, (long long)" sb->len = 0;\n"); + ok = append_list(lines, (long long)" sb->data = (char*)malloc(sb->cap);\n"); + ok = append_list(lines, (long long)" sb->data[0] = '\\0';\n"); + ok = append_list(lines, (long long)" return (long long)sb;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sb_append(long long sb_ptr, long long str_ptr) {\n"); + ok = append_list(lines, (long long)" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n"); + ok = append_list(lines, (long long)" const char* s = (const char*)str_ptr;\n"); + ok = append_list(lines, (long long)" if (!s) return sb_ptr;\n"); + ok = append_list(lines, (long long)" long long slen = strlen(s);\n"); + ok = append_list(lines, (long long)" while (sb->len + slen + 1 > sb->cap) {\n"); + ok = append_list(lines, (long long)" sb->cap *= 2;\n"); + ok = append_list(lines, (long long)" sb->data = (char*)realloc(sb->data, sb->cap);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" memcpy(sb->data + sb->len, s, slen);\n"); + ok = append_list(lines, (long long)" sb->len += slen;\n"); + ok = append_list(lines, (long long)" sb->data[sb->len] = '\\0';\n"); + ok = append_list(lines, (long long)" return sb_ptr;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sb_append_int(long long sb_ptr, long long val) {\n"); + ok = append_list(lines, (long long)" char buf[32];\n"); + ok = append_list(lines, (long long)" snprintf(buf, sizeof(buf), \"%lld\", val);\n"); + ok = append_list(lines, (long long)" return ep_sb_append(sb_ptr, (long long)buf);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sb_to_string(long long sb_ptr) {\n"); + ok = append_list(lines, (long long)" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n"); + ok = append_list(lines, (long long)" char* result = (char*)malloc(sb->len + 1);\n"); + ok = append_list(lines, (long long)" memcpy(result, sb->data, sb->len + 1);\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" free(sb->data);\n"); + ok = append_list(lines, (long long)" free(sb);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sb_length(long long sb_ptr) {\n"); + ok = append_list(lines, (long long)" return ((EpStringBuilder*)sb_ptr)->len;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long str_to_ptr(long long s) { return s; }\n"); + ok = append_list(lines, (long long)"long long ptr_to_str(long long p) {\n"); + ok = append_list(lines, (long long)" if (p == 0) return (long long)strdup(\"\");\n"); + ok = append_list(lines, (long long)" char* copy = strdup((const char*)p);\n"); + ok = append_list(lines, (long long)" ep_gc_register(copy, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)copy;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long peek_byte(long long ptr, long long offset) {\n"); + ok = append_list(lines, (long long)" return (long long)((unsigned char*)ptr)[offset];\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long poke_byte(long long ptr, long long offset, long long value) {\n"); + ok = append_list(lines, (long long)" ((unsigned char*)ptr)[offset] = (unsigned char)value;\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long alloc_bytes(long long size) {\n"); + ok = append_list(lines, (long long)" return (long long)calloc((size_t)size, 1);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long free_bytes(long long ptr) {\n"); + ok = append_list(lines, (long long)" free((void*)ptr);\n"); + ok = append_list(lines, (long long)" return 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long list_to_bytes(long long list_ptr) {\n"); + ok = append_list(lines, (long long)" long long len = length_list(list_ptr);\n"); + ok = append_list(lines, (long long)" unsigned char* buf = (unsigned char*)malloc(len);\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) {\n"); + ok = append_list(lines, (long long)" buf[i] = (unsigned char)get_list(list_ptr, i);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long bytes_to_list(long long ptr, long long len) {\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" unsigned char* buf = (unsigned char*)ptr;\n"); + ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) {\n"); + ok = append_list(lines, (long long)" append_list(list, (long long)buf[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return list;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_gc_get_minor_count() {\n"); + ok = append_list(lines, (long long)" return ep_gc_minor_count;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_gc_get_major_count() {\n"); + ok = append_list(lines, (long long)" return ep_gc_major_count;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_gc_get_nursery_count() {\n"); + ok = append_list(lines, (long long)" return ep_gc_nursery_count;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long string_to_int(long long s) {\n"); + ok = append_list(lines, (long long)" if (s == 0) return 0;\n"); + ok = append_list(lines, (long long)" return atoll((const char*)s);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long read_line() {\n"); + ok = append_list(lines, (long long)" char buf[4096];\n"); + ok = append_list(lines, (long long)" if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\\0'; }\n"); + ok = append_list(lines, (long long)" size_t len = strlen(buf);\n"); + ok = append_list(lines, (long long)" if (len > 0 && buf[len-1] == '\\n') buf[len-1] = '\\0';\n"); + ok = append_list(lines, (long long)" char* result = strdup(buf);\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long read_int() {\n"); + ok = append_list(lines, (long long)" long long val = 0;\n"); + ok = append_list(lines, (long long)" scanf(\"%lld\", &val);\n"); + ok = append_list(lines, (long long)" while(getchar() != '\\n');\n"); + ok = append_list(lines, (long long)" return val;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long read_float() {\n"); + ok = append_list(lines, (long long)" double val = 0.0;\n"); + ok = append_list(lines, (long long)" scanf(\"%lf\", &val);\n"); + ok = append_list(lines, (long long)" while(getchar() != '\\n');\n"); + ok = append_list(lines, (long long)" long long result; memcpy(&result, &val, sizeof(double));\n"); + ok = append_list(lines, (long long)" return result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long ep_rt_builtins_1() { + long long lines = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&lines); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(lines); + lines = tmp_val; + } + ok = append_list(lines, (long long)"long long int_to_float(long long val) {\n"); + ok = append_list(lines, (long long)" double d = (double)val;\n"); + ok = append_list(lines, (long long)" long long result; memcpy(&result, &d, sizeof(double));\n"); + ok = append_list(lines, (long long)" return result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long float_to_int(long long val) {\n"); + ok = append_list(lines, (long long)" double d; memcpy(&d, &val, sizeof(double));\n"); + ok = append_list(lines, (long long)" return (long long)d;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ret_val = join_strings(lines); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(lines); + return ret_val; +} + +long long get_shared_runtime_source() { + long long parts = 0; + long long ok = 0; + long long ret_val = 0; + + ep_gc_push_root(&parts); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(parts); + parts = tmp_val; + } + ok = append_list(parts, ep_rt_core_0()); + ok = append_list(parts, ep_rt_core_1()); + ok = append_list(parts, ep_rt_core_2()); + ok = append_list(parts, ep_rt_core_3()); + ok = append_list(parts, ep_rt_core_4()); + ok = append_list(parts, ep_rt_core_5()); + ok = append_list(parts, ep_rt_core_6()); + ok = append_list(parts, ep_rt_core_7()); + ok = append_list(parts, ep_rt_core_8()); + ok = append_list(parts, ep_rt_core_9()); + ok = append_list(parts, ep_rt_core_10()); + ok = append_list(parts, ep_rt_core_11()); + ok = append_list(parts, ep_rt_core_12()); + ok = append_list(parts, ep_rt_core_13()); + ok = append_list(parts, ep_rt_core_14()); + ok = append_list(parts, ep_rt_core_15()); + ok = append_list(parts, ep_rt_core_16()); + ok = append_list(parts, ep_rt_core_17()); + ok = append_list(parts, ep_rt_core_18()); + ok = append_list(parts, ep_rt_core_19()); + ok = append_list(parts, ep_rt_core_20()); + ok = append_list(parts, ep_rt_core_21()); + ok = append_list(parts, ep_rt_core_22()); + ok = append_list(parts, ep_rt_core_23()); + ok = append_list(parts, ep_rt_core_24()); + ok = append_list(parts, ep_rt_core_25()); + ok = append_list(parts, ep_rt_core_26()); + ok = append_list(parts, ep_rt_core_27()); + ok = append_list(parts, ep_rt_core_28()); + ok = append_list(parts, ep_rt_core_29()); + ok = append_list(parts, ep_rt_core_30()); + ok = append_list(parts, ep_rt_core_31()); + ok = append_list(parts, ep_rt_builtins_0()); + ok = append_list(parts, ep_rt_builtins_1()); + ret_val = join_strings(parts); + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(parts); + return ret_val; +} + + +/* Bootstrapper C main */ +int main(int argc, char** argv) { + init_ep_args(argc, argv); + int result = (int)_main(); + ep_gc_shutdown(); + return result; +} diff --git a/bootstrap/verify.sh b/bootstrap/verify.sh new file mode 100755 index 0000000..c8692ad --- /dev/null +++ b/bootstrap/verify.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# verify.sh — prove the toolchain is fully self-contained (Rust retirable). +# +# Chain, using ONLY clang: +# boot0 = clang(bootstrap/epc_bootstrap.c) +# boot1 = boot0 compiling epc.ep +# boot2 = boot1 compiling epc.ep (must be byte-identical to a 3rd stage) +# boot3 = boot2 compiling epc.ep +# assert boot2 == boot3 (fixpoint reached from the frozen C, no Rust) +# Then run the epc parity suite with the freshly bootstrapped compiler. +set -uo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" +CC="${CC:-clang}" +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT + +echo "[verify] clang-only build of the frozen bootstrap ..." +"$CC" -O2 bootstrap/epc_bootstrap.c -o "$TMP/boot0" -lpthread || { echo "FAIL: bootstrap C did not compile"; exit 1; } + +echo "[verify] boot0 -> epc.ep -> boot1 ..." +"$TMP/boot0" epc.ep >"$TMP/l1.log" 2>&1 || { echo "FAIL: boot0 cannot compile epc.ep"; grep -iE error "$TMP/l1.log" | head; exit 1; } +cp epc "$TMP/boot1" +"$TMP/boot1" epc.ep >"$TMP/l2.log" 2>&1 || { echo "FAIL: boot1 cannot compile epc.ep"; grep -iE error "$TMP/l2.log" | head; exit 1; } +cp epc "$TMP/boot2" +"$TMP/boot2" epc.ep >"$TMP/l3.log" 2>&1 || { echo "FAIL: boot2 cannot compile epc.ep"; grep -iE error "$TMP/l3.log" | head; exit 1; } +cp epc "$TMP/boot3" +cmp -s "$TMP/boot2" "$TMP/boot3" || { echo "FAIL: no fixpoint from the frozen C (boot2 != boot3)"; exit 1; } +echo "[verify] fixpoint reached from frozen C — Rust not used." + +# Freshness: the committed bootstrap must match what today's epc.ep produces, +# so the artifact can never silently drift from source. +"$TMP/boot2" epc.ep >/dev/null 2>&1 +if ! cmp -s epc_compiled.c bootstrap/epc_bootstrap.c; then + echo "FAIL: bootstrap/epc_bootstrap.c is stale — regenerate it:" + echo " ./epc epc.ep && cp epc_compiled.c bootstrap/epc_bootstrap.c" + exit 1 +fi +echo "[verify] committed bootstrap is fresh (matches epc.ep)." + +echo "[verify] running epc parity suite with the bootstrapped compiler ..." +bash tests/run_epc_parity.sh | grep -E "epc parity|rejection" +echo "[verify] OK — fully self-contained." From 3c4ecbbcf18a41a4bdd037a45612e1fd23df1605 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 00:17:09 +0100 Subject: [PATCH 16/51] docs: self-hosting status, shared runtime, and pure-C bootstrap instructions README: real self-hosting state (fixpoint, shared runtime, bootstrap/build.sh + verify.sh for the Rust-free build). AGENT.md: stronger self-hosting gates (run_fixpoint.sh, run_epc_parity.sh), shared-runtime regeneration rule, and the bootstrap freshness rule. Co-Authored-By: Claude Fable 5 --- AGENT.md | 8 ++++++++ README.md | 41 ++++++++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/AGENT.md b/AGENT.md index fc40c54..b757112 100644 --- a/AGENT.md +++ b/AGENT.md @@ -56,10 +56,18 @@ If a function isn't registered in `src/type_check.rs` (`register_builtins`) AND ```bash # This MUST pass after any change to the type system, parser, or codegen: cargo run -- epc.ep && ./epc tests/test_basic_math.ep && ./test_basic_math + +# Stronger gates (run after any epc-visible change): +bash tests/run_fixpoint.sh # 3-stage byte-identical self-compile +bash tests/run_epc_parity.sh # self-hosted coverage scoreboard (must not regress) ``` The self-hosted compiler is 5,800+ lines of real ErnosPlain that exercises the full type system, all builtin functions, list operations, string operations, struct creation, pattern matching, and closures. If it doesn't compile, you broke something. +**Shared runtime.** `runtime/ep_runtime.c` + `runtime/ep_builtins.c` are the single source of truth for the emitted C runtime. The Rust compiler embeds them via `include_str!`; the self-hosted compiler embeds them via the generated `ep_runtime_gen.ep`. After editing either `runtime/*.c` file, regenerate: `./target/release/ernos tools/gen_runtime_ep.ep && ./tools/gen_runtime_ep`. + +**Bootstrap freshness.** `bootstrap/epc_bootstrap.c` is `epc` compiled by `epc`. After ANY change that alters epc's output, regenerate it in the same commit: `./epc epc.ep && cp epc_compiled.c bootstrap/epc_bootstrap.c`. `bash bootstrap/verify.sh` enforces freshness and the clang-only fixpoint. + ### Rule 5: Read Source, Never Guess When you need to know how something works: diff --git a/README.md b/README.md index 683c41f..21bf3a8 100644 --- a/README.md +++ b/README.md @@ -330,20 +330,39 @@ Source (.ep) Ernos compiles its own compiler. The self-hosted compiler modules: -- `ep_lexer.ep` — Lexer (540 lines) -- `ep_parser.ep` — Parser (1,396 lines) -- `ep_codegen.ep` — Code generator (3,622 lines) -- `epc.ep` — Compiler driver (266 lines) -- **Total: 5,824 lines of ErnosPlain** +- `ep_lexer.ep` — Lexer (incl. f-string desugaring, English keyword aliases) +- `ep_parser.ep` — Parser (incl. `import "x" as alias`) +- `ep_codegen.ep` — Code generator (closures, enums, the shared runtime) +- `epc.ep` — Compiler driver (module flattening, aliased imports) + +The self-hosted compiler and the Rust compiler **share one C runtime** +(`runtime/ep_runtime.c` + `runtime/ep_builtins.c`), embedded by the Rust +compiler via `include_str!` and by the self-hosted compiler via the generated +`ep_runtime_gen.ep` (regenerate with `tools/gen_runtime_ep.ep`). So both emit +the same generational GC, pointer-safe accessors, and OOM-guarded allocators. + +### Stable fixpoint + +`epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), +verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is +tracked by `tests/run_epc_parity.sh`. + +### Fully self-contained bootstrap (no Rust required) + +`bootstrap/epc_bootstrap.c` is `epc` compiled by `epc` — the whole toolchain +builds with only a C compiler: -### Bootstrap ```bash -# The Rust compiler compiles the self-hosted compiler -./target/release/ernos epc.ep +bash bootstrap/build.sh # clang bootstrap/epc_bootstrap.c -> epc, then epc rebuilds epc.ep +bash bootstrap/verify.sh # proves the clang-only 3-stage fixpoint + artifact freshness +``` -# The self-hosted compiler can compile programs -./epc hello.ep -./hello +The Rust compiler builds the same self-hosted compiler and is still used for the +LSP and the cross-language transpilers: + +```bash +./target/release/ernos epc.ep # Rust compiler builds epc +./epc hello.ep && ./hello # epc compiles programs ``` --- From 59240616b7ed2f2dfff99a0d7d0ab6559a9c60f3 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 09:13:35 +0100 Subject: [PATCH 17/51] feat(self-host): floats end-to-end (parity 35->36/54) - ep_lexer.ep: float literal tokenization (digits '.' digits -> TOK_FLOAT 70). - ep_parser.ep: NODE_FLOAT (42) carrying the literal text. - ep_codegen.ep: NODE_FLOAT emits a double punned into long long; NODE_BINARY detects float operands and unpacks/computes/repacks (matching the Rust ABI); infer_type returns TYPE_FLOAT (8) for float literals and float arithmetic; display uses %.15g for floats; int_to_float and ep_dlcall_f* registered as float-returning. - Regenerated bootstrap/epc_bootstrap.c (freshness rule). Gates: FIXPOINT OK; parity 35->36/54 (test_float_ffi); run_tests.sh 69/69. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 122 ++++++++++++++++++++++++++++++++++++-- ep_codegen.ep | 73 +++++++++++++++++++++-- ep_lexer.ep | 16 ++++- ep_parser.ep | 8 ++- 4 files changed, 203 insertions(+), 16 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index d7328a5..d9ea192 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -6134,6 +6134,8 @@ long long tokenize_source(long long source) { long long should_emit_nl = 0; long long last_tok = 0; long long num_start = 0; + long long num_type = 0; + long long frac_ch = 0; long long num_len = 0; long long num_str = 0; long long is_id_start = 0; @@ -6266,9 +6268,24 @@ long long tokenize_source(long long source) { pos = (pos + 1LL); current_col = (current_col + 1LL); } + num_type = 25LL; + if ((pos + 1LL) < source_len) { + if (get_character((char*)source, pos) == 46LL) { + frac_ch = get_character((char*)source, (pos + 1LL)); + if ((frac_ch > 47LL && frac_ch < 58LL)) { + num_type = 70LL; + pos = (pos + 1LL); + current_col = (current_col + 1LL); + while (((pos < source_len && get_character((char*)source, pos) > 47LL) && get_character((char*)source, pos) < 58LL)) { + pos = (pos + 1LL); + current_col = (current_col + 1LL); + } + } + } + } num_len = (pos - num_start); num_str = (long long)substring((char*)source, num_start, num_len); - tok = (create_token(25LL, num_str, current_line, (current_col - num_len)) + 0LL); + tok = (create_token(num_type, num_str, current_line, (current_col - num_len)) + 0LL); ok = append_list(tokens, tok); } else { is_id_start = (((c > 96LL && c < 123LL) || (c > 64LL && c < 91LL)) || c == 95LL); @@ -9218,6 +9235,7 @@ long long parse_prefix(long long state) { long long tok = 0; long long t = 0; long long val = 0; + long long fnode = 0; long long ok = 0; long long chan = 0; long long target = 0; @@ -9253,6 +9271,13 @@ long long parse_prefix(long long state) { ret_val = (make_node_int(val) + 0LL); goto L_cleanup; } + if (t == 70LL) { + fnode = (create_list() + 0LL); + ok = append_list(fnode, 42LL); + ok = append_list(fnode, get_token_value(tok)); + ret_val = (fnode + 0LL); + goto L_cleanup; + } if (t == 26LL) { ret_val = (make_node_str(get_token_value(tok)) + 0LL); goto L_cleanup; @@ -10445,7 +10470,14 @@ long long analyze_return_types(long long state, long long program) { ok = map_put(keys, values, (long long)"ep_time_year", 1LL); ok = map_put(keys, values, (long long)"sleep_ms", 1LL); ok = map_put(keys, values, (long long)"ep_system", 1LL); - ok = map_put(keys, values, (long long)"int_to_float", 1LL); + ok = map_put(keys, values, (long long)"int_to_float", 8LL); + ok = map_put(keys, values, (long long)"ep_dlcall_f0", 8LL); + ok = map_put(keys, values, (long long)"ep_dlcall_f1", 8LL); + ok = map_put(keys, values, (long long)"ep_dlcall_f2", 8LL); + ok = map_put(keys, values, (long long)"ep_dlcall_f3", 8LL); + ok = map_put(keys, values, (long long)"ep_dlcall_f4", 8LL); + ok = map_put(keys, values, (long long)"ep_dlcall_f5", 8LL); + ok = map_put(keys, values, (long long)"ep_dlcall_f6", 8LL); ok = map_put(keys, values, (long long)"float_to_int", 1LL); ok = map_put(keys, values, (long long)"ep_hmac_sha256", 3LL); ok = map_put(keys, values, (long long)"ep_base64_encode", 3LL); @@ -10670,6 +10702,8 @@ long long infer_type(long long state, long long expr, long long var_keys, long l long long type = 0; long long name = 0; long long t = 0; + long long fl = 0; + long long fr = 0; long long func_keys = 0; long long func_values = 0; long long inner = 0; @@ -10696,7 +10730,21 @@ long long infer_type(long long state, long long expr, long long var_keys, long l ret_val = 1LL; goto L_cleanup; } - if (((type == 4LL || type == 5LL) || type == 14LL)) { + if (type == 42LL) { + ret_val = 8LL; + goto L_cleanup; + } + if (type == 4LL) { + fl = infer_type(state, get_list(expr, 1LL), var_keys, var_values); + fr = infer_type(state, get_list(expr, 3LL), var_keys, var_values); + if ((fl == 8LL || fr == 8LL)) { + ret_val = 8LL; + goto L_cleanup; + } + ret_val = 1LL; + goto L_cleanup; + } + if ((type == 5LL || type == 14LL)) { ret_val = 1LL; goto L_cleanup; } @@ -11473,6 +11521,12 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon line = string_concat(line, (long long)");\n"); ok = emit(state, line); } else { + if (t == 8LL) { + line = (long long)" { long long _ftmp = "; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)"; double _dv; memcpy(&_dv, &_ftmp, sizeof(double)); printf(\"%.15g\\n\", _dv); }\n"); + ok = emit(state, line); + } else { if (t == 7LL) { line = (long long)" printf(\"%s\\n\", ("; line = string_concat(line, expr_str); @@ -11485,6 +11539,7 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon ok = emit(state, line); } } + } ret_val = 0LL; goto L_cleanup; } @@ -11731,17 +11786,23 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon long long type = 0; long long val = 0; long long num_str = 0; + long long fres = 0; long long escaped = 0; long long res = 0; long long name = 0; long long eres = 0; long long fk = 0; - long long fres = 0; long long left = 0; long long op = 0; long long right = 0; long long left_str = 0; long long right_str = 0; + long long flt = 0; + long long frt = 0; + long long fop = 0; + long long lu = 0; + long long ru = 0; + long long fr2 = 0; long long op_str = 0; long long lt = 0; long long is_string = 0; @@ -11843,12 +11904,15 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon long long elem_str = 0; long long ret_val = 0; + ep_gc_push_root(&fres); ep_gc_push_root(&escaped); ep_gc_push_root(&res); ep_gc_push_root(&eres); - ep_gc_push_root(&fres); ep_gc_push_root(&left_str); ep_gc_push_root(&right_str); + ep_gc_push_root(&lu); + ep_gc_push_root(&ru); + ep_gc_push_root(&fr2); ep_gc_push_root(&formatted_args); ep_gc_push_root(&arg_val); ep_gc_push_root(&casted); @@ -11885,6 +11949,13 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon ret_val = string_concat(num_str, (long long)"LL"); goto L_cleanup; } + if (type == 42LL) { + fres = (long long)"({ double _fl = "; + fres = string_concat(fres, get_list(expr, 1LL)); + fres = string_concat(fres, (long long)"; long long _fv; memcpy(&_fv, &_fl, sizeof(double)); _fv; })"); + ret_val = fres; + goto L_cleanup; + } if (type == 2LL) { val = get_list(expr, 1LL); escaped = escape_string(val); @@ -11923,6 +11994,45 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon right = get_list(expr, 3LL); left_str = gen_expr(state, left, var_keys, var_values); right_str = gen_expr(state, right, var_keys, var_values); + flt = infer_type(state, left, var_keys, var_values); + frt = infer_type(state, right, var_keys, var_values); + if ((flt == 8LL || frt == 8LL)) { + fop = (long long)"+"; + if (op == 2LL) { + fop = (long long)"-"; + } + if (op == 3LL) { + fop = (long long)"*"; + } + if (op == 4LL) { + fop = (long long)"/"; + } + lu = (long long)""; + if (flt == 8LL) { + lu = string_concat((long long)"({ long long _lt = ", left_str); + lu = string_concat(lu, (long long)"; double _d; memcpy(&_d, &_lt, sizeof(double)); _d; })"); + } else { + lu = string_concat((long long)"(double)(", left_str); + lu = string_concat(lu, (long long)")"); + } + ru = (long long)""; + if (frt == 8LL) { + ru = string_concat((long long)"({ long long _rt = ", right_str); + ru = string_concat(ru, (long long)"; double _d; memcpy(&_d, &_rt, sizeof(double)); _d; })"); + } else { + ru = string_concat((long long)"(double)(", right_str); + ru = string_concat(ru, (long long)")"); + } + fr2 = (long long)"({ double _r = "; + fr2 = string_concat(fr2, lu); + fr2 = string_concat(fr2, (long long)" "); + fr2 = string_concat(fr2, fop); + fr2 = string_concat(fr2, (long long)" "); + fr2 = string_concat(fr2, ru); + fr2 = string_concat(fr2, (long long)"; long long _v; memcpy(&_v, &_r, sizeof(double)); _v; })"); + ret_val = fr2; + goto L_cleanup; + } op_str = (long long)""; if (op == 1LL) { op_str = (long long)"+"; @@ -12489,7 +12599,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon ret_val = (long long)""; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(33); + ep_gc_pop_roots(36); free_list(formatted_args); free_list(args_str_list); free_list(raw_names); diff --git a/ep_codegen.ep b/ep_codegen.ep index f563e14..3c0b6e1 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -438,7 +438,14 @@ define analyze_return_types with state and program: set ok to map_put(keys and values and "ep_time_year" and 1) set ok to map_put(keys and values and "sleep_ms" and 1) set ok to map_put(keys and values and "ep_system" and 1) - set ok to map_put(keys and values and "int_to_float" and 1) + set ok to map_put(keys and values and "int_to_float" and 8) + set ok to map_put(keys and values and "ep_dlcall_f0" and 8) + set ok to map_put(keys and values and "ep_dlcall_f1" and 8) + set ok to map_put(keys and values and "ep_dlcall_f2" and 8) + set ok to map_put(keys and values and "ep_dlcall_f3" and 8) + set ok to map_put(keys and values and "ep_dlcall_f4" and 8) + set ok to map_put(keys and values and "ep_dlcall_f5" and 8) + set ok to map_put(keys and values and "ep_dlcall_f6" and 8) set ok to map_put(keys and values and "float_to_int" and 1) # Crypto (hex/encoded strings) set ok to map_put(keys and values and "ep_hmac_sha256" and 3) @@ -615,7 +622,15 @@ define infer_type with state and expr and var_keys and var_values: if t != 0: return t return 1 - if type == 4 || type == 5 || type == 14: # NODE_BINARY, NODE_COMP, NODE_LOGICAL + if type == 42: # NODE_FLOAT -> TYPE_FLOAT = 8 + return 8 + if type == 4: # NODE_BINARY: float-typed when either operand is float, else int + set fl to infer_type(state and get_list(expr and 1) and var_keys and var_values) + set fr to infer_type(state and get_list(expr and 3) and var_keys and var_values) + if fl == 8 || fr == 8: + return 8 + return 1 + if type == 5 || type == 14: # NODE_COMP, NODE_LOGICAL return 1 if type == 31 || type == 32: # NODE_BOOL_LIT, NODE_UNARY_NOT -> TYPE_BOOL = 7 return 7 @@ -1116,12 +1131,18 @@ define gen_statement with state and stmt and var_keys and var_values: set line to string_concat(line and ");\n") set ok to emit(state and line) else: - if t == 7: # TYPE_BOOL - set line to " printf(\"%s\\n\", (" + if t == 8: # TYPE_FLOAT + set line to " { long long _ftmp = " set line to string_concat(line and expr_str) - set line to string_concat(line and ") ? \"true\" : \"false\");\n") + set line to string_concat(line and "; double _dv; memcpy(&_dv, &_ftmp, sizeof(double)); printf(\"%.15g\\n\", _dv); }\n") set ok to emit(state and line) else: + if t == 7: # TYPE_BOOL + set line to " printf(\"%s\\n\", (" + set line to string_concat(line and expr_str) + set line to string_concat(line and ") ? \"true\" : \"false\");\n") + set ok to emit(state and line) + else: set line to " printf(\"%lld\\n\", " set line to string_concat(line and expr_str) set line to string_concat(line and ");\n") @@ -1363,6 +1384,12 @@ define gen_expr with state and expr and var_keys and var_values: set val to get_list(expr and 1) set num_str to cg_int_to_str(val) return string_concat(num_str and "LL") + + if type == 42: # NODE_FLOAT — double literal punned into a long long + set fres to "({ double _fl = " + set fres to string_concat(fres and get_list(expr and 1)) + set fres to string_concat(fres and "; long long _fv; memcpy(&_fv, &_fl, sizeof(double)); _fv; })") + return fres if type == 2: # NODE_STR set val to get_list(expr and 1) @@ -1399,7 +1426,41 @@ define gen_expr with state and expr and var_keys and var_values: set left_str to gen_expr(state and left and var_keys and var_values) set right_str to gen_expr(state and right and var_keys and var_values) - + + # Float arithmetic: unpack punned doubles, compute, repack (mirrors Rust) + set flt to infer_type(state and left and var_keys and var_values) + set frt to infer_type(state and right and var_keys and var_values) + if flt == 8 || frt == 8: + set fop to "+" + if op == 2: + set fop to "-" + if op == 3: + set fop to "*" + if op == 4: + set fop to "/" + set lu to "" + if flt == 8: + set lu to string_concat("({ long long _lt = " and left_str) + set lu to string_concat(lu and "; double _d; memcpy(&_d, &_lt, sizeof(double)); _d; })") + else: + set lu to string_concat("(double)(" and left_str) + set lu to string_concat(lu and ")") + set ru to "" + if frt == 8: + set ru to string_concat("({ long long _rt = " and right_str) + set ru to string_concat(ru and "; double _d; memcpy(&_d, &_rt, sizeof(double)); _d; })") + else: + set ru to string_concat("(double)(" and right_str) + set ru to string_concat(ru and ")") + set fr2 to "({ double _r = " + set fr2 to string_concat(fr2 and lu) + set fr2 to string_concat(fr2 and " ") + set fr2 to string_concat(fr2 and fop) + set fr2 to string_concat(fr2 and " ") + set fr2 to string_concat(fr2 and ru) + set fr2 to string_concat(fr2 and "; long long _v; memcpy(&_v, &_r, sizeof(double)); _v; })") + return fr2 + set op_str to "" if op == 1: set op_str to "+" diff --git a/ep_lexer.ep b/ep_lexer.ep index 9915bb6..472e934 100644 --- a/ep_lexer.ep +++ b/ep_lexer.ep @@ -331,16 +331,26 @@ define tokenize_source with source: repeat while pos < source_len && get_character(source and pos) != 10 && get_character(source and pos) != 13: set pos to pos + 1 else: - # Parse Number + # Parse Number (integer, or float when digits '.' digits) if c > 47 && c < 58: set num_start to pos repeat while pos < source_len && get_character(source and pos) > 47 && get_character(source and pos) < 58: set pos to pos + 1 set current_col to current_col + 1 - + set num_type to 25 # TOK_INTEGER + if pos + 1 < source_len: + if get_character(source and pos) == 46: # '.' + set frac_ch to get_character(source and pos + 1) + if frac_ch > 47 && frac_ch < 58: + set num_type to 70 # TOK_FLOAT + set pos to pos + 1 + set current_col to current_col + 1 + repeat while pos < source_len && get_character(source and pos) > 47 && get_character(source and pos) < 58: + set pos to pos + 1 + set current_col to current_col + 1 set num_len to pos - num_start set num_str to substring(source and num_start and num_len) - set tok to create_token(25 and num_str and current_line and current_col - num_len) + 0 # TOK_INTEGER = 25 + set tok to create_token(num_type and num_str and current_line and current_col - num_len) + 0 set ok to append_list(tokens and tok) else: # Parse Identifier or Keyword diff --git a/ep_parser.ep b/ep_parser.ep index d1ea00e..a5644ef 100644 --- a/ep_parser.ep +++ b/ep_parser.ep @@ -1209,7 +1209,13 @@ define parse_prefix with state: if t == 25: # TOK_INTEGER set val to parse_int(get_token_value(tok)) return make_node_int(val) + 0 - + + if t == 70: # TOK_FLOAT — keep the literal text; codegen embeds it verbatim + set fnode to create_list() + 0 + set ok to append_list(fnode and 42) # NODE_FLOAT = 42 + set ok to append_list(fnode and get_token_value(tok)) + return fnode + 0 + if t == 26: # TOK_STRING_LITERAL return make_node_str(get_token_value(tok)) + 0 From 3a8f661c12f25a0de44c38de25b5cbd684e751ce Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 09:40:14 +0100 Subject: [PATCH 18/51] =?UTF-8?q?feat(self-host):=20semantic=20checker=20p?= =?UTF-8?q?ass=20=E2=80=94=20epc=20now=20enforces=20hard=20errors=20(rejec?= =?UTF-8?q?tion=204->8/9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New ep_check.ep runs between parse and codegen (imported by epc.ep). Mirrors the Rust type checker's hard-error classes it can decide structurally: - reserved-keyword shadowing: 'channel' as a variable or parameter name - Send safety (E0036): a borrowed reference sent to spawn/send - heterogeneous list literals: string mixed with non-string elements Walks functions, methods, and nested control flow. Prints 'Type Error: ...' diagnostics and aborts compilation on any finding; valid programs unaffected. Remaining wrongly-accepted: test_enum_type (needs full enum-payload return-type inference — deferred to the deeper type-inference work). Gates: FIXPOINT OK; parity 36/54 held; rejection 4->8 of 9; run_tests.sh 69/69. Bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 307 ++++++++++++++++++++++++++++++++++++++ ep_check.ep | 151 +++++++++++++++++++ epc.ep | 9 +- 3 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 ep_check.ep diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index d9ea192..7f86ebf 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -4970,6 +4970,11 @@ long long parse_prefix(long long); long long parse_closure(long long); long long parse_struct_create(long long); long long parse_list_literal(long long); +long long check_lit_category(long long); +long long check_expr(long long, long long, long long); +long long check_stmts(long long, long long); +long long check_function(long long, long long); +long long check_program(long long); long long map_get(long long, long long, long long); long long map_contains_key(long long, long long); long long collect_idents_expr(long long, long long); @@ -5468,6 +5473,7 @@ long long _main() { long long ok = 0; long long empty_imports = 0; long long program_ast = 0; + long long check_ok = 0; long long c_code = 0; long long c_path = 0; long long compile_cmd = 0; @@ -5611,6 +5617,12 @@ long long _main() { ok = append_list(program_ast, all_method_defs); ok = append_list(program_ast, all_trait_defs); ok = append_list(program_ast, all_trait_impls); + check_ok = check_program(program_ast); + if (check_ok == 0LL) { + printf("%s\n", (char*)(long long)"Compilation failed: semantic errors."); + ret_val = 1LL; + goto L_cleanup; + } printf("%s\n", (char*)(long long)"[2/3] Generating C Source..."); c_code = generate_c(program_ast, is_test_mode); if (string_length((char*)c_code) == 0LL) { @@ -9580,6 +9592,301 @@ long long parse_list_literal(long long state) { return ret_val; } +long long check_lit_category(long long expr) { + long long t = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + t = get_list(expr, 0LL); + if (t == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (t == 42LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (t == 31LL) { + ret_val = 1LL; + goto L_cleanup; + } + if (t == 2LL) { + ret_val = 2LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long check_expr(long long expr, long long errs, long long in_spawn_arg) { + long long t = 0; + long long ok = 0; + long long elems = 0; + long long n = 0; + long long saw_str = 0; + long long saw_num = 0; + long long i = 0; + long long el = 0; + long long cat = 0; + long long oke = 0; + long long okl = 0; + long long okr = 0; + long long args = 0; + long long an = 0; + long long ai = 0; + long long oka = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + if (expr == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + t = get_list(expr, 0LL); + if (t == 20LL) { + if (in_spawn_arg == 1LL) { + ok = append_list(errs, (long long)"Send safety (E0036): a borrowed reference is not Send and cannot be sent to a spawned thread"); + } + ret_val = check_expr(get_list(expr, 1LL), errs, 0LL); + goto L_cleanup; + } + if (t == 35LL) { + elems = get_list(expr, 1LL); + n = length_list(elems); + saw_str = 0LL; + saw_num = 0LL; + i = 0LL; + while (i < n) { + el = get_list(elems, i); + cat = check_lit_category(el); + if (cat == 1LL) { + saw_num = 1LL; + } + if (cat == 2LL) { + saw_str = 1LL; + } + oke = check_expr(el, errs, 0LL); + i = (i + 1LL); + } + if (saw_str == 1LL) { + if (saw_num == 1LL) { + ok = append_list(errs, (long long)"list elements have conflicting types (string mixed with non-string)"); + } + } + ret_val = 0LL; + goto L_cleanup; + } + if (((t == 4LL || t == 5LL) || t == 14LL)) { + okl = check_expr(get_list(expr, 1LL), errs, 0LL); + okr = check_expr(get_list(expr, 3LL), errs, 0LL); + ret_val = 0LL; + goto L_cleanup; + } + if (t == 6LL) { + args = get_list(expr, 2LL); + an = length_list(args); + ai = 0LL; + while (ai < an) { + oka = check_expr(get_list(args, ai), errs, 0LL); + ai = (ai + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + if ((((t == 18LL || t == 21LL) || t == 32LL) || t == 33LL)) { + ret_val = check_expr(get_list(expr, 1LL), errs, 0LL); + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long check_stmts(long long stmts, long long errs) { + long long n = 0; + long long idx = 0; + long long stmt = 0; + long long t = 0; + long long tgt = 0; + long long ok = 0; + long long okv = 0; + long long okr = 0; + long long okc = 0; + long long okt = 0; + long long eb = 0; + long long oke = 0; + long long okw = 0; + long long okwb = 0; + long long sargs = 0; + long long sn = 0; + long long si = 0; + long long oks = 0; + long long okn = 0; + long long okfe = 0; + long long okfeb = 0; + long long arms = 0; + long long arn = 0; + long long ari = 0; + long long okam = 0; + long long ret_val = 0; + + ep_gc_push_root(&tgt); + ep_gc_maybe_collect(); + + n = length_list(stmts); + idx = 0LL; + while (idx < n) { + stmt = get_list(stmts, idx); + t = get_list(stmt, 0LL); + if (t == 7LL) { + tgt = string_concat(get_list(stmt, 1LL), (long long)""); + if ((strcmp((char*)tgt, (char*)(long long)"channel") == 0)) { + ok = append_list(errs, (long long)"cannot shadow the reserved keyword 'channel' (use it as a channel, not a variable name)"); + } + okv = check_expr(get_list(stmt, 2LL), errs, 0LL); + } + if (((t == 8LL || t == 9LL) || t == 36LL)) { + okr = check_expr(get_list(stmt, 1LL), errs, 0LL); + } + if (t == 10LL) { + okc = check_expr(get_list(stmt, 1LL), errs, 0LL); + okt = check_stmts(get_list(stmt, 2LL), errs); + eb = get_list(stmt, 3LL); + if (eb != 0LL) { + oke = check_stmts(eb, errs); + } + } + if (t == 11LL) { + okw = check_expr(get_list(stmt, 1LL), errs, 0LL); + okwb = check_stmts(get_list(stmt, 2LL), errs); + } + if (t == 15LL) { + sargs = get_list(stmt, 2LL); + sn = length_list(sargs); + si = 0LL; + while (si < sn) { + oks = check_expr(get_list(sargs, si), errs, 1LL); + si = (si + 1LL); + } + } + if (t == 16LL) { + okn = check_expr(get_list(stmt, 2LL), errs, 1LL); + } + if (t == 28LL) { + okfe = check_expr(get_list(stmt, 2LL), errs, 0LL); + okfeb = check_stmts(get_list(stmt, 3LL), errs); + } + if (t == 27LL) { + arms = get_list(stmt, 2LL); + arn = length_list(arms); + ari = 0LL; + while (ari < arn) { + okam = check_stmts(get_list(get_list(arms, ari), 2LL), errs); + ari = (ari + 1LL); + } + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long check_function(long long func, long long errs) { + long long params = 0; + long long pn = 0; + long long pi = 0; + long long pname = 0; + long long ok = 0; + long long okb = 0; + long long ret_val = 0; + + ep_gc_push_root(&pname); + ep_gc_maybe_collect(); + + params = get_list(func, 2LL); + pn = length_list(params); + pi = 0LL; + while (pi < pn) { + pname = string_concat(get_list(get_list(params, pi), 0LL), (long long)""); + if ((strcmp((char*)pname, (char*)(long long)"channel") == 0)) { + ok = append_list(errs, (long long)"cannot use the reserved keyword 'channel' as a parameter name"); + } + pi = (pi + 1LL); + } + okb = check_stmts(get_list(func, 3LL), errs); + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long check_program(long long program) { + long long errs = 0; + long long funcs = 0; + long long n = 0; + long long idx = 0; + long long okf = 0; + long long methods = 0; + long long mn = 0; + long long mi = 0; + long long mdef = 0; + long long okm = 0; + long long e_len = 0; + long long ei = 0; + long long ret_val = 0; + + ep_gc_push_root(&errs); + ep_gc_maybe_collect(); + + { + long long tmp_val = create_list(); + free_list(errs); + errs = tmp_val; + } + funcs = get_list(program, 3LL); + n = length_list(funcs); + idx = 0LL; + while (idx < n) { + okf = check_function(get_list(funcs, idx), errs); + idx = (idx + 1LL); + } + if (length_list(program) > 6LL) { + methods = get_list(program, 6LL); + mn = length_list(methods); + mi = 0LL; + while (mi < mn) { + mdef = get_list(methods, mi); + okm = check_stmts(get_list(mdef, 4LL), errs); + mi = (mi + 1LL); + } + } + e_len = length_list(errs); + if (e_len == 0LL) { + ret_val = 1LL; + goto L_cleanup; + } + ei = 0LL; + while (ei < e_len) { + printf("%s\n", (char*)concat((long long)"Type Error: ", get_list(errs, ei))); + ei = (ei + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(errs); + return ret_val; +} + long long map_get(long long keys, long long values, long long key) { long long key_str = 0; long long len = 0; diff --git a/ep_check.ep b/ep_check.ep new file mode 100644 index 0000000..f290e96 --- /dev/null +++ b/ep_check.ep @@ -0,0 +1,151 @@ +# ErnosPlain Self-Hosted Semantic Checker +# +# A pre-codegen enforcement pass mirroring the hard-error classes of the Rust +# type checker (src/type_check.rs). Returns 1 when the program is well-formed, +# 0 (after printing diagnostics) when it must be rejected. +# +# Covered so far: reserved-keyword shadowing (`channel`), Send-safety (a borrowed +# reference may not be sent to a spawned thread), and heterogeneous list literals +# (string mixed with non-string elements). + +# Coarse literal category used for list-homogeneity: 1=numeric, 2=string, 0=other. +define check_lit_category with expr: + set t to get_list(expr and 0) + if t == 1: # NODE_INT + return 1 + if t == 42: # NODE_FLOAT + return 1 + if t == 31: # NODE_BOOL_LIT + return 1 + if t == 2: # NODE_STR + return 2 + return 0 + +# Walk an expression; append a human-readable error string to `errs` for any +# Send-unsafe borrow used where `in_spawn_arg` is 1, and for heterogeneous list +# literals. Returns 0. +define check_expr with expr and errs and in_spawn_arg: + if expr == 0: + return 0 + set t to get_list(expr and 0) + if t == 20: # NODE_BORROW + if in_spawn_arg == 1: + set ok to append_list(errs and "Send safety (E0036): a borrowed reference is not Send and cannot be sent to a spawned thread") + return check_expr(get_list(expr and 1) and errs and 0) + if t == 35: # NODE_LIST_LIT — element homogeneity + set elems to get_list(expr and 1) + set n to length_list(elems) + set saw_str to 0 + set saw_num to 0 + set i to 0 + repeat while i < n: + set el to get_list(elems and i) + set cat to check_lit_category(el) + if cat == 1: + set saw_num to 1 + if cat == 2: + set saw_str to 1 + set oke to check_expr(el and errs and 0) + set i to i + 1 + if saw_str == 1: + if saw_num == 1: + set ok to append_list(errs and "list elements have conflicting types (string mixed with non-string)") + return 0 + if t == 4 || t == 5 || t == 14: # BINARY / COMP / LOGICAL + set okl to check_expr(get_list(expr and 1) and errs and 0) + set okr to check_expr(get_list(expr and 3) and errs and 0) + return 0 + if t == 6: # NODE_CALL + set args to get_list(expr and 2) + set an to length_list(args) + set ai to 0 + repeat while ai < an: + set oka to check_expr(get_list(args and ai) and errs and 0) + set ai to ai + 1 + return 0 + if t == 18 || t == 21 || t == 32 || t == 33: # RECEIVE / AWAIT / NOT / TRY + return check_expr(get_list(expr and 1) and errs and 0) + return 0 + +define check_stmts with stmts and errs: + set n to length_list(stmts) + set idx to 0 + repeat while idx < n: + set stmt to get_list(stmts and idx) + set t to get_list(stmt and 0) + if t == 7: # NODE_SET — reserved-name target + value walk + set tgt to string_concat(get_list(stmt and 1) and "") + if tgt equals "channel": + set ok to append_list(errs and "cannot shadow the reserved keyword 'channel' (use it as a channel, not a variable name)") + set okv to check_expr(get_list(stmt and 2) and errs and 0) + if t == 8 || t == 9 || t == 36: # RETURN / DISPLAY / EXPR_STMT + set okr to check_expr(get_list(stmt and 1) and errs and 0) + if t == 10: # IF + set okc to check_expr(get_list(stmt and 1) and errs and 0) + set okt to check_stmts(get_list(stmt and 2) and errs) + set eb to get_list(stmt and 3) + if eb != 0: + set oke to check_stmts(eb and errs) + if t == 11: # REPEAT_WHILE + set okw to check_expr(get_list(stmt and 1) and errs and 0) + set okwb to check_stmts(get_list(stmt and 2) and errs) + if t == 15: # SPAWN — args checked in spawn context (Send safety) + set sargs to get_list(stmt and 2) + set sn to length_list(sargs) + set si to 0 + repeat while si < sn: + set oks to check_expr(get_list(sargs and si) and errs and 1) + set si to si + 1 + if t == 16: # SEND — value is sent across threads too + set okn to check_expr(get_list(stmt and 2) and errs and 1) + if t == 28: # FOR_EACH + set okfe to check_expr(get_list(stmt and 2) and errs and 0) + set okfeb to check_stmts(get_list(stmt and 3) and errs) + if t == 27: # MATCH + set arms to get_list(stmt and 2) + set arn to length_list(arms) + set ari to 0 + repeat while ari < arn: + set okam to check_stmts(get_list(get_list(arms and ari) and 2) and errs) + set ari to ari + 1 + set idx to idx + 1 + return 0 + +define check_function with func and errs: + set params to get_list(func and 2) + set pn to length_list(params) + set pi to 0 + repeat while pi < pn: + set pname to string_concat(get_list(get_list(params and pi) and 0) and "") + if pname equals "channel": + set ok to append_list(errs and "cannot use the reserved keyword 'channel' as a parameter name") + set pi to pi + 1 + set okb to check_stmts(get_list(func and 3) and errs) + return 0 + +# Entry point: returns 1 if OK, 0 (after printing) if the program is rejected. +define check_program with program: + set errs to create_list() + set funcs to get_list(program and 3) + set n to length_list(funcs) + set idx to 0 + repeat while idx < n: + set okf to check_function(get_list(funcs and idx) and errs) + set idx to idx + 1 + # methods (index 6) share the function shape + if length_list(program) > 6: + set methods to get_list(program and 6) + set mn to length_list(methods) + set mi to 0 + repeat while mi < mn: + set mdef to get_list(methods and mi) + set okm to check_stmts(get_list(mdef and 4) and errs) + set mi to mi + 1 + set e_len to length_list(errs) + if e_len == 0: + return 1 + set ei to 0 + repeat while ei < e_len: + display concat("Type Error: " and get_list(errs and ei)) + set ei to ei + 1 + return 0 diff --git a/epc.ep b/epc.ep index 091db02..bcab2ef 100644 --- a/epc.ep +++ b/epc.ep @@ -1,6 +1,7 @@ # ErnosPlain Self-Hosted Compiler Driver import "ep_lexer" import "ep_parser" +import "ep_check" import "ep_codegen" import "ep_runtime_gen" @@ -267,9 +268,15 @@ define main: set ok to append_list(program_ast and all_trait_defs) set ok to append_list(program_ast and all_trait_impls) + # Semantic checking (hard errors) before codegen. + set check_ok to check_program(program_ast) + if check_ok == 0: + display "Compilation failed: semantic errors." + return 1 + display "[2/3] Generating C Source..." set c_code to generate_c(program_ast and is_test_mode) - + if string_length(c_code) == 0: display "Compilation failed due to errors." return 1 From d906d1de95b277400abbce25c64cc37b49283c7f Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 09:46:49 +0100 Subject: [PATCH 19/51] feat(self-host): 'epc check ' subcommand (Phase 8 tooling) Runs lexing/parsing/semantic checks with no codegen and reports 'Check passed' or the type errors, reusing ep_check.check_program. First self-hosted developer tool beyond the compiler driver. Gates: FIXPOINT OK; parity 36/54; run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 16 ++++++++++++++++ epc.ep | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 7f86ebf..919d325 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -5453,6 +5453,7 @@ long long _main() { long long arg_count = 0; long long first_arg = 0; long long is_test_mode = 0; + long long check_only = 0; long long input_path = 0; long long stem = 0; long long all_functions = 0; @@ -5517,6 +5518,7 @@ long long _main() { } first_arg = (long long)get_argument(1LL); is_test_mode = 0LL; + check_only = 0LL; input_path = (long long)get_argument(1LL); if ((strcmp((char*)(long long)"test", (char*)first_arg) == 0)) { if (arg_count < 3LL) { @@ -5527,6 +5529,15 @@ long long _main() { is_test_mode = 1LL; input_path = (long long)get_argument(2LL); } + if ((strcmp((char*)(long long)"check", (char*)first_arg) == 0)) { + if (arg_count < 3LL) { + printf("%s\n", (char*)(long long)"Usage: epc check "); + ret_val = 1LL; + goto L_cleanup; + } + check_only = 1LL; + input_path = (long long)get_argument(2LL); + } stem = get_file_stem(input_path); printf("%s\n", (char*)(long long)"[1/3] Tokenizing and Parsing..."); { @@ -5623,6 +5634,11 @@ long long _main() { ret_val = 1LL; goto L_cleanup; } + if (check_only == 1LL) { + printf("%s\n", (char*)(long long)"Check passed: no errors."); + ret_val = 0LL; + goto L_cleanup; + } printf("%s\n", (char*)(long long)"[2/3] Generating C Source..."); c_code = generate_c(program_ast, is_test_mode); if (string_length((char*)c_code) == 0LL) { diff --git a/epc.ep b/epc.ep index bcab2ef..244d02f 100644 --- a/epc.ep +++ b/epc.ep @@ -214,14 +214,23 @@ define main: set first_arg to get_argument(1) set is_test_mode to 0 + set check_only to 0 set input_path to get_argument(1) - + if "test" equals first_arg: if arg_count < 3: display "Usage: epc test " return 1 set is_test_mode to 1 set input_path to get_argument(2) + + # `epc check `: run lexing/parsing/semantic checks only, no codegen. + if "check" equals first_arg: + if arg_count < 3: + display "Usage: epc check " + return 1 + set check_only to 1 + set input_path to get_argument(2) set stem to get_file_stem(input_path) @@ -274,6 +283,10 @@ define main: display "Compilation failed: semantic errors." return 1 + if check_only == 1: + display "Check passed: no errors." + return 0 + display "[2/3] Generating C Source..." set c_code to generate_c(program_ast and is_test_mode) From c8b3e1523c0ea4556ef713a7c90b1f114d23d781 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 09:54:02 +0100 Subject: [PATCH 20/51] docs: record self-hosted parity (36/54) and epc check in README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 21bf3a8..ed42d3b 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,9 @@ the same generational GC, pointer-safe accessors, and OOM-guarded allocators. `epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh`. +tracked by `tests/run_epc_parity.sh` (currently 36/54 runnable programs; 8/9 +compile-error tests correctly rejected by the `ep_check.ep` semantic pass). +`epc check ` runs the checks without codegen. ### Fully self-contained bootstrap (no Rust required) From cc813a1b75a179c5cc1e25e62214b24dde4d099b Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 11:00:34 +0100 Subject: [PATCH 21/51] feat(self-host): constant-folding optimizer + fix negative cg_int_to_str (Phase 7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ep_optimizer.ep folds literal-OP-literal integer arithmetic at the AST level (imported by epc.ep, runs after the checker). Identity eliminations like x + 0 -> x are deliberately omitted: the self-hosted compiler's + 0 pointer-coercion idiom must survive (a heap handle is never an integer literal, so literal-only folding can't touch it — verified: xs + 0 still coerces). Folding 0 - 1 -> -1 exposed a latent bug: cg_int_to_str's 'while temp > 0' loop never ran for negatives and returned '' (emitting a bare 'LL'). Fixed to emit a leading '-' over the magnitude. Gates: FIXPOINT OK; parity 36/54 with outputs unchanged (behavior-preserving); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 240 +++++++++++++++++++++++++++++++++++++- ep_codegen.ep | 10 +- ep_optimizer.ep | 122 +++++++++++++++++++ epc.ep | 4 + 4 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 ep_optimizer.ep diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 919d325..0807ec2 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -4975,6 +4975,9 @@ long long check_expr(long long, long long, long long); long long check_stmts(long long, long long); long long check_function(long long, long long); long long check_program(long long); +long long opt_fold_expr(long long); +long long opt_fold_stmts(long long); +long long optimize_program(long long); long long map_get(long long, long long, long long); long long map_contains_key(long long, long long); long long collect_idents_expr(long long, long long); @@ -5083,7 +5086,7 @@ long long get_file_stem(long long path) { ep_gc_maybe_collect(); len = string_length((char*)path); - last_slash = (0LL - 1LL); + last_slash = -1LL; idx = 0LL; while (idx < len) { ch = get_character((char*)path, idx); @@ -5121,7 +5124,7 @@ long long get_file_dir(long long path) { ep_gc_maybe_collect(); len = string_length((char*)path); - last_slash = (0LL - 1LL); + last_slash = -1LL; idx = 0LL; while (idx < len) { ch = get_character((char*)path, idx); @@ -5475,6 +5478,7 @@ long long _main() { long long empty_imports = 0; long long program_ast = 0; long long check_ok = 0; + long long opt_ok = 0; long long c_code = 0; long long c_path = 0; long long compile_cmd = 0; @@ -5639,6 +5643,7 @@ long long _main() { ret_val = 0LL; goto L_cleanup; } + opt_ok = optimize_program(program_ast); printf("%s\n", (char*)(long long)"[2/3] Generating C Source..."); c_code = generate_c(program_ast, is_test_mode); if (string_length((char*)c_code) == 0LL) { @@ -9903,6 +9908,227 @@ long long check_program(long long program) { return ret_val; } +long long opt_fold_expr(long long expr) { + long long t = 0; + long long left = 0; + long long right = 0; + long long ok1 = 0; + long long ok3 = 0; + long long op = 0; + long long lv = 0; + long long rv = 0; + long long folded = 0; + long long res = 0; + long long ok5l = 0; + long long ok5r = 0; + long long args = 0; + long long an = 0; + long long ai = 0; + long long okca = 0; + long long ok1c = 0; + long long elems = 0; + long long en = 0; + long long ei = 0; + long long okle = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + if (expr == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + t = get_list(expr, 0LL); + if (t == 4LL) { + left = opt_fold_expr(get_list(expr, 1LL)); + right = opt_fold_expr(get_list(expr, 3LL)); + ok1 = set_list(expr, 1LL, left); + ok3 = set_list(expr, 3LL, right); + op = get_list(expr, 2LL); + if (get_list(left, 0LL) == 1LL) { + if (get_list(right, 0LL) == 1LL) { + lv = get_list(left, 1LL); + rv = get_list(right, 1LL); + folded = 0LL; + res = 0LL; + if (op == 1LL) { + res = (lv + rv); + folded = 1LL; + } + if (op == 2LL) { + res = (lv - rv); + folded = 1LL; + } + if (op == 3LL) { + res = (lv * rv); + folded = 1LL; + } + if (op == 4LL) { + if (rv != 0LL) { + res = (lv / rv); + folded = 1LL; + } + } + if (op == 5LL) { + if (rv != 0LL) { + res = (lv - ((lv / rv) * rv)); + folded = 1LL; + } + } + if (folded == 1LL) { + ret_val = (make_node_int(res) + 0LL); + goto L_cleanup; + } + } + } + ret_val = expr; + goto L_cleanup; + } + if ((t == 5LL || t == 14LL)) { + ok5l = set_list(expr, 1LL, opt_fold_expr(get_list(expr, 1LL))); + ok5r = set_list(expr, 3LL, opt_fold_expr(get_list(expr, 3LL))); + ret_val = expr; + goto L_cleanup; + } + if (t == 6LL) { + args = get_list(expr, 2LL); + an = length_list(args); + ai = 0LL; + while (ai < an) { + okca = set_list(args, ai, opt_fold_expr(get_list(args, ai))); + ai = (ai + 1LL); + } + ret_val = expr; + goto L_cleanup; + } + if ((((t == 18LL || t == 21LL) || t == 32LL) || t == 33LL)) { + ok1c = set_list(expr, 1LL, opt_fold_expr(get_list(expr, 1LL))); + ret_val = expr; + goto L_cleanup; + } + if (t == 35LL) { + elems = get_list(expr, 1LL); + en = length_list(elems); + ei = 0LL; + while (ei < en) { + okle = set_list(elems, ei, opt_fold_expr(get_list(elems, ei))); + ei = (ei + 1LL); + } + ret_val = expr; + goto L_cleanup; + } + ret_val = expr; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long opt_fold_stmts(long long stmts) { + long long n = 0; + long long idx = 0; + long long stmt = 0; + long long t = 0; + long long oks = 0; + long long okr = 0; + long long okc = 0; + long long okt = 0; + long long eb = 0; + long long oke = 0; + long long okw = 0; + long long okwb = 0; + long long okse = 0; + long long okfe = 0; + long long okfeb = 0; + long long arms = 0; + long long arn = 0; + long long ari = 0; + long long okam = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + n = length_list(stmts); + idx = 0LL; + while (idx < n) { + stmt = get_list(stmts, idx); + t = get_list(stmt, 0LL); + if (t == 7LL) { + oks = set_list(stmt, 2LL, opt_fold_expr(get_list(stmt, 2LL))); + } + if (((t == 8LL || t == 9LL) || t == 36LL)) { + okr = set_list(stmt, 1LL, opt_fold_expr(get_list(stmt, 1LL))); + } + if (t == 10LL) { + okc = set_list(stmt, 1LL, opt_fold_expr(get_list(stmt, 1LL))); + okt = opt_fold_stmts(get_list(stmt, 2LL)); + eb = get_list(stmt, 3LL); + if (eb != 0LL) { + oke = opt_fold_stmts(eb); + } + } + if (t == 11LL) { + okw = set_list(stmt, 1LL, opt_fold_expr(get_list(stmt, 1LL))); + okwb = opt_fold_stmts(get_list(stmt, 2LL)); + } + if (t == 16LL) { + okse = set_list(stmt, 2LL, opt_fold_expr(get_list(stmt, 2LL))); + } + if (t == 28LL) { + okfe = set_list(stmt, 2LL, opt_fold_expr(get_list(stmt, 2LL))); + okfeb = opt_fold_stmts(get_list(stmt, 3LL)); + } + if (t == 27LL) { + arms = get_list(stmt, 2LL); + arn = length_list(arms); + ari = 0LL; + while (ari < arn) { + okam = opt_fold_stmts(get_list(get_list(arms, ari), 2LL)); + ari = (ari + 1LL); + } + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long optimize_program(long long program) { + long long funcs = 0; + long long n = 0; + long long idx = 0; + long long okf = 0; + long long methods = 0; + long long mn = 0; + long long mi = 0; + long long okm = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + funcs = get_list(program, 3LL); + n = length_list(funcs); + idx = 0LL; + while (idx < n) { + okf = opt_fold_stmts(get_list(get_list(funcs, idx), 3LL)); + idx = (idx + 1LL); + } + if (length_list(program) > 6LL) { + methods = get_list(program, 6LL); + mn = length_list(methods); + mi = 0LL; + while (mi < mn) { + okm = opt_fold_stmts(get_list(get_list(methods, mi), 4LL)); + mi = (mi + 1LL); + } + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + long long map_get(long long keys, long long values, long long key) { long long key_str = 0; long long len = 0; @@ -10304,6 +10530,7 @@ long long get_fn_c_name(long long func) { } long long cg_int_to_str(long long n) { + long long neg = 0; long long lst = 0; long long temp = 0; long long digits = 0; @@ -10323,6 +10550,11 @@ long long cg_int_to_str(long long n) { ret_val = (long long)"0"; goto L_cleanup; } + neg = 0LL; + if (n < 0LL) { + neg = 1LL; + n = (0LL - n); + } { long long tmp_val = create_list(); free_list(lst); @@ -10346,6 +10578,10 @@ long long cg_int_to_str(long long n) { len = (len - 1LL); } res = (long long)string_from_list(lst); + if (neg == 1LL) { + ret_val = string_concat((long long)"-", res); + goto L_cleanup; + } ret_val = res; goto L_cleanup; L_cleanup: diff --git a/ep_codegen.ep b/ep_codegen.ep index 3c0b6e1..37c672d 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -198,6 +198,12 @@ define get_fn_c_name with func: define cg_int_to_str with n: if n == 0: return "0" + # Negative values: emit a leading '-' and format the magnitude. (Without + # this the digit loop `while temp > 0` never ran and returned "".) + set neg to 0 + if n < 0: + set neg to 1 + set n to 0 - n set lst to create_list() set temp to n set digits to create_list() @@ -211,8 +217,10 @@ define cg_int_to_str with n: set d to pop_list(digits) set ok to append_list(lst and d) set len to len - 1 - + set res to string_from_list(lst) + if neg == 1: + return string_concat("-" and res) return res define escape_string with s: diff --git a/ep_optimizer.ep b/ep_optimizer.ep new file mode 100644 index 0000000..d7efa10 --- /dev/null +++ b/ep_optimizer.ep @@ -0,0 +1,122 @@ +# ErnosPlain Self-Hosted Optimizer +# +# AST-level constant folding, mirroring the safe core of src/optimizer.rs. +# +# IMPORTANT: only literal-OP-literal is folded. Identity eliminations like +# `x + 0 -> x` are deliberately NOT performed — the self-hosted compiler relies +# on the ` + 0` idiom to coerce pointers to int, and folding it +# away would change semantics. Since a bare heap handle is never an integer +# literal, literal-only folding can never touch that idiom. + +# Fold one expression in place (children mutated via set_list). Returns the +# possibly-replaced node. +define opt_fold_expr with expr: + if expr == 0: + return 0 + set t to get_list(expr and 0) + if t == 4: # NODE_BINARY [4, left, op, right] + set left to opt_fold_expr(get_list(expr and 1)) + set right to opt_fold_expr(get_list(expr and 3)) + set ok1 to set_list(expr and 1 and left) + set ok3 to set_list(expr and 3 and right) + set op to get_list(expr and 2) + if get_list(left and 0) == 1: # NODE_INT + if get_list(right and 0) == 1: + set lv to get_list(left and 1) + set rv to get_list(right and 1) + set folded to 0 + set res to 0 + if op == 1: + set res to lv + rv + set folded to 1 + if op == 2: + set res to lv - rv + set folded to 1 + if op == 3: + set res to lv * rv + set folded to 1 + if op == 4: + if rv != 0: + set res to lv / rv + set folded to 1 + if op == 5: + if rv != 0: + set res to lv - ((lv / rv) * rv) + set folded to 1 + if folded == 1: + return make_node_int(res) + 0 + return expr + if t == 5 || t == 14: # COMP / LOGICAL — fold children only + set ok5l to set_list(expr and 1 and opt_fold_expr(get_list(expr and 1))) + set ok5r to set_list(expr and 3 and opt_fold_expr(get_list(expr and 3))) + return expr + if t == 6: # NODE_CALL — fold each argument + set args to get_list(expr and 2) + set an to length_list(args) + set ai to 0 + repeat while ai < an: + set okca to set_list(args and ai and opt_fold_expr(get_list(args and ai))) + set ai to ai + 1 + return expr + if t == 18 || t == 21 || t == 32 || t == 33: # RECEIVE/AWAIT/NOT/TRY + set ok1c to set_list(expr and 1 and opt_fold_expr(get_list(expr and 1))) + return expr + if t == 35: # LIST_LIT + set elems to get_list(expr and 1) + set en to length_list(elems) + set ei to 0 + repeat while ei < en: + set okle to set_list(elems and ei and opt_fold_expr(get_list(elems and ei))) + set ei to ei + 1 + return expr + return expr + +define opt_fold_stmts with stmts: + set n to length_list(stmts) + set idx to 0 + repeat while idx < n: + set stmt to get_list(stmts and idx) + set t to get_list(stmt and 0) + if t == 7: # SET + set oks to set_list(stmt and 2 and opt_fold_expr(get_list(stmt and 2))) + if t == 8 || t == 9 || t == 36: # RETURN / DISPLAY / EXPR_STMT + set okr to set_list(stmt and 1 and opt_fold_expr(get_list(stmt and 1))) + if t == 10: # IF + set okc to set_list(stmt and 1 and opt_fold_expr(get_list(stmt and 1))) + set okt to opt_fold_stmts(get_list(stmt and 2)) + set eb to get_list(stmt and 3) + if eb != 0: + set oke to opt_fold_stmts(eb) + if t == 11: # REPEAT_WHILE + set okw to set_list(stmt and 1 and opt_fold_expr(get_list(stmt and 1))) + set okwb to opt_fold_stmts(get_list(stmt and 2)) + if t == 16: # SEND + set okse to set_list(stmt and 2 and opt_fold_expr(get_list(stmt and 2))) + if t == 28: # FOR_EACH + set okfe to set_list(stmt and 2 and opt_fold_expr(get_list(stmt and 2))) + set okfeb to opt_fold_stmts(get_list(stmt and 3)) + if t == 27: # MATCH + set arms to get_list(stmt and 2) + set arn to length_list(arms) + set ari to 0 + repeat while ari < arn: + set okam to opt_fold_stmts(get_list(get_list(arms and ari) and 2)) + set ari to ari + 1 + set idx to idx + 1 + return 0 + +define optimize_program with program: + set funcs to get_list(program and 3) + set n to length_list(funcs) + set idx to 0 + repeat while idx < n: + set okf to opt_fold_stmts(get_list(get_list(funcs and idx) and 3)) + set idx to idx + 1 + if length_list(program) > 6: + set methods to get_list(program and 6) + set mn to length_list(methods) + set mi to 0 + repeat while mi < mn: + set okm to opt_fold_stmts(get_list(get_list(methods and mi) and 4)) + set mi to mi + 1 + return 0 diff --git a/epc.ep b/epc.ep index 244d02f..ebb1d5c 100644 --- a/epc.ep +++ b/epc.ep @@ -2,6 +2,7 @@ import "ep_lexer" import "ep_parser" import "ep_check" +import "ep_optimizer" import "ep_codegen" import "ep_runtime_gen" @@ -287,6 +288,9 @@ define main: display "Check passed: no errors." return 0 + # Optimization pass (constant folding) before codegen. + set opt_ok to optimize_program(program_ast) + display "[2/3] Generating C Source..." set c_code to generate_c(program_ast and is_test_mode) From cd77f2fc413a1f3a169127ace0d30c36489fc926 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 11:34:35 +0100 Subject: [PATCH 22/51] test: align epc parity output comparison with run_tests.sh (36->40/54) run_tests.sh compares via $(...) capture, which strips trailing newlines on both sides; the parity harness diffed against the raw file, so .expected files lacking a final newline (test_enum_method, test_stdlib_import, etc.) counted as mismatches even though the programs' output is correct. Match run_tests.sh exactly. No compiler change; these tests were already passing. Co-Authored-By: Claude Fable 5 --- tests/run_epc_parity.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/run_epc_parity.sh b/tests/run_epc_parity.sh index 76bd670..6c6c686 100755 --- a/tests/run_epc_parity.sh +++ b/tests/run_epc_parity.sh @@ -70,11 +70,16 @@ for TEST_FILE in tests/test_*.ep; do [[ $VERBOSE -eq 1 ]] && echo "FAIL $NAME (exit $RC)" continue fi + # Compare like run_tests.sh: $(...) capture strips trailing newlines on both + # sides, so a missing final newline in a .expected file is not a failure. EXPECTED_FILE="tests/$NAME.expected" - if [[ -f "$EXPECTED_FILE" ]] && ! diff -q <(printf '%s\n' "$RUN_OUT") "$EXPECTED_FILE" >/dev/null 2>&1; then - FAIL=$((FAIL+1)); FAILED="$FAILED $NAME(output)" - [[ $VERBOSE -eq 1 ]] && { echo "FAIL $NAME (output mismatch)"; diff <(printf '%s\n' "$RUN_OUT") "$EXPECTED_FILE" | head -6; } - continue + if [[ -f "$EXPECTED_FILE" ]]; then + EXPECTED_FROM_FILE=$(cat "$EXPECTED_FILE") + if [[ "$RUN_OUT" != "$EXPECTED_FROM_FILE" ]]; then + FAIL=$((FAIL+1)); FAILED="$FAILED $NAME(output)" + [[ $VERBOSE -eq 1 ]] && { echo "FAIL $NAME (output mismatch)"; diff <(echo "$EXPECTED_FROM_FILE") <(echo "$RUN_OUT") | head -6; } + continue + fi fi PASS=$((PASS+1)) [[ $VERBOSE -eq 1 ]] && echo "PASS $NAME" From 2fb1a9f848ba70231a107477b5b17eea96fa379b Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 11:42:03 +0100 Subject: [PATCH 23/51] =?UTF-8?q?chore:=20final=20polish=20=E2=80=94=20tre?= =?UTF-8?q?e=20cleanup,=20.dSYM=20ignore,=20docs=20at=2040/54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove stray epc_stage1 binary; ignore tests/**/*.dSYM and /epc_stage1. - README: self-hosted parity 40/54, full lex->parse->check->optimize->codegen pipeline, and the honest remaining gap (trait vtables, struct float fields, enum return-type inference; Rust retains LSP + transpilers). Final matrix: cargo 0 warnings; run_tests.sh 69/69; fixpoint byte-identical; epc parity 40/54 (8/9 rejections); bootstrap/verify.sh fully self-contained. Co-Authored-By: Claude Fable 5 --- .gitignore | 4 ++++ README.md | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 06dadda..6b72b1f 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,7 @@ tests/* # Bootstrap build output (keep the frozen C, not the binary) /epc-bootstrapped + +# Debug bundles under tests/ (the !tests/*/ negation would otherwise re-include them) +tests/**/*.dSYM/ +/epc_stage1 diff --git a/README.md b/README.md index ed42d3b..3be657d 100644 --- a/README.md +++ b/README.md @@ -345,10 +345,16 @@ the same generational GC, pointer-safe accessors, and OOM-guarded allocators. `epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh` (currently 36/54 runnable programs; 8/9 +tracked by `tests/run_epc_parity.sh` (currently 40/54 runnable programs; 8/9 compile-error tests correctly rejected by the `ep_check.ep` semantic pass). `epc check ` runs the checks without codegen. +The self-hosted pipeline is `lex → parse → check → optimize → codegen` +(`ep_lexer` · `ep_parser` · `ep_check` · `ep_optimizer` · `ep_codegen`). The +remaining coverage gap is trait vtables, struct float fields, and full enum +return-type inference; the Rust compiler covers those plus the LSP and +cross-language transpilers. + ### Fully self-contained bootstrap (no Rust required) `bootstrap/epc_bootstrap.c` is `epc` compiled by `epc` — the whole toolchain From d591734d689b297fc4ea0a4d80a7a8365825ce23 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 12:04:56 +0100 Subject: [PATCH 24/51] feat(self-host): top-level constant globals (parity 40->41/54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Top-level 'set NAME to expr' statements are now parsed as global constants (program AST index 9), threaded through parse_all_modules, and emitted by codegen as file-scope globals with GC mark functions (__ep_mark_globals_*) and an __ep_init_constants() run from main before user code — mirroring the Rust compiler. Enables mutable global collections (the bridge-library pattern). Gates: FIXPOINT OK; parity 40->41/54 (test_global_mutation); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 142 ++++++++++++++++++++++++++++++++++++-- ep_codegen.ep | 59 ++++++++++++++++ ep_parser.ep | 17 +++-- epc.ep | 19 +++-- 4 files changed, 220 insertions(+), 17 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 0807ec2..53c7244 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -4877,6 +4877,14 @@ long long float_to_int(long long val) { } #define EP_STRUCT_MAX_SLOTS 8 +static void __ep_mark_globals_major(void) { +} +static void __ep_mark_globals_minor(void) { +} +void __ep_init_constants(void) { + ep_gc_mark_globals_major = __ep_mark_globals_major; + ep_gc_mark_globals_minor = __ep_mark_globals_minor; +} /* External Function Prototypes (FFI) */ @@ -4886,7 +4894,7 @@ long long get_file_stem(long long); long long get_file_dir(long long); long long contains_string(long long, long long); long long resolve_import_path(long long, long long); -long long parse_all_modules(long long, long long, long long, long long, long long, long long, long long, long long, long long); +long long parse_all_modules(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long); long long _main(); long long create_token(long long, long long, long long, long long); long long get_token_type(long long); @@ -5205,7 +5213,7 @@ long long resolve_import_path(long long current_file, long long import_path) { return ret_val; } -long long parse_all_modules(long long current_file, long long parsed_files, long long all_functions, long long all_externals, long long all_struct_defs, long long all_enum_defs, long long all_method_defs, long long all_trait_defs, long long all_trait_impls) { +long long parse_all_modules(long long current_file, long long parsed_files, long long all_functions, long long all_externals, long long all_struct_defs, long long all_enum_defs, long long all_method_defs, long long all_trait_defs, long long all_trait_impls, long long all_constants) { long long has_parsed = 0; long long ok = 0; long long source = 0; @@ -5237,6 +5245,9 @@ long long parse_all_modules(long long current_file, long long parsed_files, long long long ti = 0; long long ti_len = 0; long long ti_idx = 0; + long long tc = 0; + long long tc_len = 0; + long long tc_idx = 0; long long imp_len = 0; long long i_idx = 0; long long imp_pair = 0; @@ -5374,6 +5385,15 @@ long long parse_all_modules(long long current_file, long long parsed_files, long ti_idx = (ti_idx + 1LL); } } + if (prog_len > 9LL) { + tc = get_list(program_ast, 9LL); + tc_len = length_list(tc); + tc_idx = 0LL; + while (tc_idx < tc_len) { + ok = append_list(all_constants, get_list(tc, tc_idx)); + tc_idx = (tc_idx + 1LL); + } + } imp_len = length_list(imports); i_idx = 0LL; while (i_idx < imp_len) { @@ -5382,7 +5402,7 @@ long long parse_all_modules(long long current_file, long long parsed_files, long imp_alias = string_concat(get_list(imp_pair, 1LL), (long long)""); resolved_path = resolve_import_path(current_file, imp); if (string_length((char*)imp_alias) == 0LL) { - status = parse_all_modules(resolved_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls); + status = parse_all_modules(resolved_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants); if (status != 0LL) { ret_val = status; goto L_cleanup; @@ -5398,7 +5418,7 @@ long long parse_all_modules(long long current_file, long long parsed_files, long free_list(mod_externals); mod_externals = tmp_val; } - status = parse_all_modules(resolved_path, parsed_files, mod_funcs, mod_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls); + status = parse_all_modules(resolved_path, parsed_files, mod_funcs, mod_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants); if (status != 0LL) { ret_val = status; goto L_cleanup; @@ -5466,6 +5486,7 @@ long long _main() { long long all_method_defs = 0; long long all_trait_defs = 0; long long all_trait_impls = 0; + long long all_constants = 0; long long parsed_files = 0; long long status = 0; long long f_names = 0; @@ -5501,6 +5522,7 @@ long long _main() { ep_gc_push_root(&all_method_defs); ep_gc_push_root(&all_trait_defs); ep_gc_push_root(&all_trait_impls); + ep_gc_push_root(&all_constants); ep_gc_push_root(&parsed_files); ep_gc_push_root(&f_names); ep_gc_push_root(&empty_imports); @@ -5579,12 +5601,17 @@ long long _main() { free_list(all_trait_impls); all_trait_impls = tmp_val; } + { + long long tmp_val = create_list(); + free_list(all_constants); + all_constants = tmp_val; + } { long long tmp_val = create_list(); free_list(parsed_files); parsed_files = tmp_val; } - status = parse_all_modules(input_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls); + status = parse_all_modules(input_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants); if (status != 0LL) { ret_val = 1LL; goto L_cleanup; @@ -5632,6 +5659,7 @@ long long _main() { ok = append_list(program_ast, all_method_defs); ok = append_list(program_ast, all_trait_defs); ok = append_list(program_ast, all_trait_impls); + ok = append_list(program_ast, all_constants); check_ok = check_program(program_ast); if (check_ok == 0LL) { printf("%s\n", (char*)(long long)"Compilation failed: semantic errors."); @@ -5702,7 +5730,7 @@ long long _main() { goto L_cleanup; } L_cleanup: - ep_gc_pop_roots(19); + ep_gc_pop_roots(20); free_list(all_functions); free_list(all_externals); free_list(all_struct_defs); @@ -5710,6 +5738,7 @@ long long _main() { free_list(all_method_defs); free_list(all_trait_defs); free_list(all_trait_impls); + free_list(all_constants); free_list(parsed_files); free_list(f_names); free_list(empty_imports); @@ -7138,6 +7167,7 @@ long long make_node_program(long long imports, long long externals, long long fu ok = append_list(node, method_defs); ok = append_list(node, trait_defs); ok = append_list(node, trait_impls); + ok = append_list(node, (create_list() + 0LL)); ret_val = node; node = 0; goto L_cleanup; @@ -8193,6 +8223,7 @@ long long parse_program(long long state) { long long method_defs = 0; long long trait_defs = 0; long long trait_impls = 0; + long long top_constants = 0; long long loop = 0; long long tok = 0; long long t = 0; @@ -8223,6 +8254,7 @@ long long parse_program(long long state) { long long t3 = 0; long long method_def = 0; long long impl_node = 0; + long long const_stmt = 0; long long prog = 0; long long ret_val = 0; @@ -8236,6 +8268,7 @@ long long parse_program(long long state) { method_defs = (create_list() + 0LL); trait_defs = (create_list() + 0LL); trait_impls = (create_list() + 0LL); + top_constants = (create_list() + 0LL); loop = 1LL; while (loop == 1LL) { tok = peek_token(state); @@ -8330,6 +8363,10 @@ long long parse_program(long long state) { impl_node = (parse_trait_impl(state) + 0LL); ok = append_list(trait_impls, impl_node); } else { + if (t == 2LL) { + const_stmt = (parse_statement(state) + 0LL); + ok = append_list(top_constants, const_stmt); + } else { printf("%s\n", (char*)(long long)"Parser Error: Unexpected token at top level:"); printf("%lld\n", t); ok = set_parser_error(state); @@ -8342,7 +8379,9 @@ long long parse_program(long long state) { } } } + } prog = (make_node_program(imports, externals, funcs, struct_defs, enum_defs, method_defs, trait_defs, trait_impls) + 0LL); + ok = set_list(prog, 9LL, top_constants); ret_val = prog; goto L_cleanup; L_cleanup: @@ -13195,8 +13234,10 @@ long long get_c_main_source() { lines = tmp_val; } ok = append_list(lines, (long long)"\n/* Bootstrapper C main */\n"); + ok = append_list(lines, (long long)"void __ep_init_constants(void);\n"); ok = append_list(lines, (long long)"int main(int argc, char** argv) {\n"); ok = append_list(lines, (long long)" init_ep_args(argc, argv);\n"); + ok = append_list(lines, (long long)" __ep_init_constants();\n"); ok = append_list(lines, (long long)" int result = (int)_main();\n"); ok = append_list(lines, (long long)" ep_gc_shutdown();\n"); ok = append_list(lines, (long long)" return result;\n"); @@ -14230,6 +14271,17 @@ long long generate_c(long long program, long long is_test_mode) { long long ts_len = 0; long long ts_idx = 0; long long tname = 0; + long long constants = 0; + long long const_n = 0; + long long ci = 0; + long long cstmt = 0; + long long gline = 0; + long long gn = 0; + long long ml = 0; + long long gk = 0; + long long gv = 0; + long long ival = 0; + long long il = 0; long long externals = 0; long long ext_len = 0; long long idx = 0; @@ -14285,6 +14337,12 @@ long long generate_c(long long program, long long is_test_mode) { ep_gc_push_root(&line); ep_gc_push_root(&slot_line); ep_gc_push_root(&tag_slots); + ep_gc_push_root(&gline); + ep_gc_push_root(&ml); + ep_gc_push_root(&gk); + ep_gc_push_root(&gv); + ep_gc_push_root(&ival); + ep_gc_push_root(&il); ep_gc_push_root(&proto); ep_gc_push_root(&proto2); ep_gc_push_root(&proto3); @@ -14389,6 +14447,72 @@ long long generate_c(long long program, long long is_test_mode) { } } } + constants = create_list(); + if (length_list(program) > 9LL) { + constants = get_list(program, 9LL); + } + const_n = length_list(constants); + ci = 0LL; + while (ci < const_n) { + cstmt = get_list(constants, ci); + gline = (long long)"long long "; + gline = string_concat(gline, get_list(cstmt, 1LL)); + gline = string_concat(gline, (long long)" = 0;\n"); + ok = emit(state, gline); + ci = (ci + 1LL); + } + ok = emit(state, (long long)"static void __ep_mark_globals_major(void) {\n"); + ci = 0LL; + while (ci < const_n) { + gn = get_list(get_list(constants, ci), 1LL); + ml = (long long)" if ("; + ml = string_concat(ml, gn); + ml = string_concat(ml, (long long)" != 0) ep_gc_mark_object((void*)"); + ml = string_concat(ml, gn); + ml = string_concat(ml, (long long)");\n"); + ok = emit(state, ml); + ci = (ci + 1LL); + } + ok = emit(state, (long long)"}\n"); + ok = emit(state, (long long)"static void __ep_mark_globals_minor(void) {\n"); + ci = 0LL; + while (ci < const_n) { + gn = get_list(get_list(constants, ci), 1LL); + ml = (long long)" if ("; + ml = string_concat(ml, gn); + ml = string_concat(ml, (long long)" != 0) ep_gc_mark_object_minor((void*)"); + ml = string_concat(ml, gn); + ml = string_concat(ml, (long long)");\n"); + ok = emit(state, ml); + ci = (ci + 1LL); + } + ok = emit(state, (long long)"}\n"); + ok = emit(state, (long long)"void __ep_init_constants(void) {\n"); + ok = emit(state, (long long)" ep_gc_mark_globals_major = __ep_mark_globals_major;\n"); + ok = emit(state, (long long)" ep_gc_mark_globals_minor = __ep_mark_globals_minor;\n"); + { + long long tmp_val = create_list(); + free_list(gk); + gk = tmp_val; + } + { + long long tmp_val = create_list(); + free_list(gv); + gv = tmp_val; + } + ci = 0LL; + while (ci < const_n) { + cstmt = get_list(constants, ci); + ival = gen_expr(state, get_list(cstmt, 2LL), gk, gv); + il = (long long)" "; + il = string_concat(il, get_list(cstmt, 1LL)); + il = string_concat(il, (long long)" = "); + il = string_concat(il, ival); + il = string_concat(il, (long long)";\n"); + ok = emit(state, il); + ci = (ci + 1LL); + } + ok = emit(state, (long long)"}\n"); externals = get_list(program, 2LL); ext_len = length_list(externals); ok = emit(state, (long long)"\n/* External Function Prototypes (FFI) */\n"); @@ -14584,10 +14708,12 @@ long long generate_c(long long program, long long is_test_mode) { ret_val = c_code; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(13); + ep_gc_pop_roots(19); free_list(state); free_list(field_slots); free_list(tag_slots); + free_list(gk); + free_list(gv); free_list(spawn_list); return ret_val; } @@ -20241,8 +20367,10 @@ long long get_shared_runtime_source() { /* Bootstrapper C main */ +void __ep_init_constants(void); int main(int argc, char** argv) { init_ep_args(argc, argv); + __ep_init_constants(); int result = (int)_main(); ep_gc_shutdown(); return result; diff --git a/ep_codegen.ep b/ep_codegen.ep index 37c672d..e63ceb4 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -1961,8 +1961,10 @@ define get_c_runtime_source: define get_c_main_source: set lines to create_list() set ok to append_list(lines and "\n/* Bootstrapper C main */\n") + set ok to append_list(lines and "void __ep_init_constants(void);\n") set ok to append_list(lines and "int main(int argc, char** argv) {\n") set ok to append_list(lines and " init_ep_args(argc, argv);\n") + set ok to append_list(lines and " __ep_init_constants();\n") set ok to append_list(lines and " int result = (int)_main();\n") set ok to append_list(lines and " ep_gc_shutdown();\n") set ok to append_list(lines and " return result;\n") @@ -2623,6 +2625,63 @@ define generate_c with program and is_test_mode: set ok to emit(state and line) set ts_idx to ts_idx + 1 + # Emit top-level constant globals (file scope), their GC mark functions, and + # an __ep_init_constants() that runs their initializers. Always emit the init + # function (empty if no globals) so main can unconditionally call it. + set constants to create_list() + if length_list(program) > 9: + set constants to get_list(program and 9) + set const_n to length_list(constants) + set ci to 0 + repeat while ci < const_n: + set cstmt to get_list(constants and ci) + set gline to "long long " + set gline to string_concat(gline and get_list(cstmt and 1)) + set gline to string_concat(gline and " = 0;\n") + set ok to emit(state and gline) + set ci to ci + 1 + set ok to emit(state and "static void __ep_mark_globals_major(void) {\n") + set ci to 0 + repeat while ci < const_n: + set gn to get_list(get_list(constants and ci) and 1) + set ml to " if (" + set ml to string_concat(ml and gn) + set ml to string_concat(ml and " != 0) ep_gc_mark_object((void*)") + set ml to string_concat(ml and gn) + set ml to string_concat(ml and ");\n") + set ok to emit(state and ml) + set ci to ci + 1 + set ok to emit(state and "}\n") + set ok to emit(state and "static void __ep_mark_globals_minor(void) {\n") + set ci to 0 + repeat while ci < const_n: + set gn to get_list(get_list(constants and ci) and 1) + set ml to " if (" + set ml to string_concat(ml and gn) + set ml to string_concat(ml and " != 0) ep_gc_mark_object_minor((void*)") + set ml to string_concat(ml and gn) + set ml to string_concat(ml and ");\n") + set ok to emit(state and ml) + set ci to ci + 1 + set ok to emit(state and "}\n") + set ok to emit(state and "void __ep_init_constants(void) {\n") + set ok to emit(state and " ep_gc_mark_globals_major = __ep_mark_globals_major;\n") + set ok to emit(state and " ep_gc_mark_globals_minor = __ep_mark_globals_minor;\n") + set gk to create_list() + set gv to create_list() + set ci to 0 + repeat while ci < const_n: + set cstmt to get_list(constants and ci) + set ival to gen_expr(state and get_list(cstmt and 2) and gk and gv) + set il to " " + set il to string_concat(il and get_list(cstmt and 1)) + set il to string_concat(il and " = ") + set il to string_concat(il and ival) + set il to string_concat(il and ";\n") + set ok to emit(state and il) + set ci to ci + 1 + set ok to emit(state and "}\n") + # Emit FFI external prototypes set externals to get_list(program and 2) set ext_len to length_list(externals) diff --git a/ep_parser.ep b/ep_parser.ep index a5644ef..a976c4a 100644 --- a/ep_parser.ep +++ b/ep_parser.ep @@ -151,6 +151,7 @@ define make_node_program with imports and externals and funcs and struct_defs an set ok to append_list(node and method_defs) set ok to append_list(node and trait_defs) set ok to append_list(node and trait_impls) + set ok to append_list(node and (create_list() + 0)) # index 9: top-level constants return node define make_node_spawn with func_name and args: @@ -506,6 +507,7 @@ define parse_program with state: set method_defs to create_list() + 0 set trait_defs to create_list() + 0 set trait_impls to create_list() + 0 + set top_constants to create_list() + 0 set loop to 1 repeat while loop == 1: set tok to peek_token(state) @@ -597,12 +599,17 @@ define parse_program with state: set impl_node to parse_trait_impl(state) + 0 set ok to append_list(trait_impls and impl_node) else: - display "Parser Error: Unexpected token at top level:" - display t - set ok to set_parser_error(state) - set loop to 0 - + if t == 2: # top-level `set` -> global constant + set const_stmt to parse_statement(state) + 0 + set ok to append_list(top_constants and const_stmt) + else: + display "Parser Error: Unexpected token at top level:" + display t + set ok to set_parser_error(state) + set loop to 0 + set prog to make_node_program(imports and externals and funcs and struct_defs and enum_defs and method_defs and trait_defs and trait_impls) + 0 + set ok to set_list(prog and 9 and top_constants) return prog # define structure Name: diff --git a/epc.ep b/epc.ep index ebb1d5c..8c84764 100644 --- a/epc.ep +++ b/epc.ep @@ -68,7 +68,7 @@ define resolve_import_path with current_file and import_path: return resolved return string_concat(resolved and ".ep") -define parse_all_modules with current_file and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls: +define parse_all_modules with current_file and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants: set has_parsed to contains_string(parsed_files and current_file) if has_parsed == 1: return 0 @@ -147,7 +147,14 @@ define parse_all_modules with current_file and parsed_files and all_functions an repeat while ti_idx < ti_len: set ok to append_list(all_trait_impls and get_list(ti and ti_idx)) set ti_idx to ti_idx + 1 - + if prog_len > 9: + set tc to get_list(program_ast and 9) + set tc_len to length_list(tc) + set tc_idx to 0 + repeat while tc_idx < tc_len: + set ok to append_list(all_constants and get_list(tc and tc_idx)) + set tc_idx to tc_idx + 1 + set imp_len to length_list(imports) set i_idx to 0 repeat while i_idx < imp_len: @@ -157,7 +164,7 @@ define parse_all_modules with current_file and parsed_files and all_functions an set imp_alias to string_concat(get_list(imp_pair and 1) and "") set resolved_path to resolve_import_path(current_file and imp) if string_length(imp_alias) == 0: - set status to parse_all_modules(resolved_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls) + set status to parse_all_modules(resolved_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants) if status != 0: return status else: @@ -167,7 +174,7 @@ define parse_all_modules with current_file and parsed_files and all_functions an # the Rust driver. set mod_funcs to create_list() set mod_externals to create_list() - set status to parse_all_modules(resolved_path and parsed_files and mod_funcs and mod_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls) + set status to parse_all_modules(resolved_path and parsed_files and mod_funcs and mod_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants) if status != 0: return status set mf_len to length_list(mod_funcs) @@ -243,8 +250,9 @@ define main: set all_method_defs to create_list() set all_trait_defs to create_list() set all_trait_impls to create_list() + set all_constants to create_list() set parsed_files to create_list() - set status to parse_all_modules(input_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls) + set status to parse_all_modules(input_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants) if status != 0: return 1 @@ -277,6 +285,7 @@ define main: set ok to append_list(program_ast and all_method_defs) set ok to append_list(program_ast and all_trait_defs) set ok to append_list(program_ast and all_trait_impls) + set ok to append_list(program_ast and all_constants) # Semantic checking (hard errors) before codegen. set check_ok to check_program(program_ast) From 94de27d3bca942dd33efa8ef936ed477c70dcaa9 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 12:12:08 +0100 Subject: [PATCH 25/51] feat(self-host): sanitize C-keyword identifiers (parity 41->42/54) A user function named after a C keyword (e.g. 'double') emitted invalid C ('long long double(...)'). New cg_sanitize_name prefixes C keywords / libc clashes with ep_ (mirrors the Rust sanitize_c_name), applied at definitions, prototypes, call sites, and function-pointer references. The membership test contains_string_val hit the same unknown-typed-'equals' ->pointer-compare bug as before; coerce the left operand via string_concat so it strcmp's. Gates: FIXPOINT OK; parity 41->42/54 (test_hof_named); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 115 +++++++++++++++++++++++++++++++++++--- ep_codegen.ep | 81 +++++++++++++++++++++++++-- 2 files changed, 183 insertions(+), 13 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 53c7244..618b8f1 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -4993,6 +4993,8 @@ long long collect_idents_stmts(long long, long long); long long map_put(long long, long long, long long, long long); long long field_slot_index(long long, long long); long long string_concat(long long, long long); +long long cg_sanitize_name(long long); +long long contains_string_val(long long, long long); long long get_fn_c_name(long long); long long cg_int_to_str(long long); long long escape_string(long long); @@ -10551,18 +10553,117 @@ long long string_concat(long long s1, long long s2) { return ret_val; } -long long get_fn_c_name(long long func) { - long long name = 0; +long long cg_sanitize_name(long long name) { + long long n = 0; + long long kws = 0; + long long ok = 0; long long ret_val = 0; + ep_gc_push_root(&n); + ep_gc_push_root(&kws); ep_gc_maybe_collect(); - name = get_list(func, 1LL); - if ((strcmp((char*)(long long)"main", (char*)name) == 0)) { + n = string_concat(name, (long long)""); + if ((strcmp((char*)(long long)"main", (char*)n) == 0)) { ret_val = (long long)"_main"; goto L_cleanup; } - ret_val = name; + { + long long tmp_val = create_list(); + free_list(kws); + kws = tmp_val; + } + ok = append_list(kws, (long long)"auto"); + ok = append_list(kws, (long long)"break"); + ok = append_list(kws, (long long)"case"); + ok = append_list(kws, (long long)"char"); + ok = append_list(kws, (long long)"const"); + ok = append_list(kws, (long long)"continue"); + ok = append_list(kws, (long long)"default"); + ok = append_list(kws, (long long)"do"); + ok = append_list(kws, (long long)"double"); + ok = append_list(kws, (long long)"else"); + ok = append_list(kws, (long long)"enum"); + ok = append_list(kws, (long long)"extern"); + ok = append_list(kws, (long long)"float"); + ok = append_list(kws, (long long)"for"); + ok = append_list(kws, (long long)"goto"); + ok = append_list(kws, (long long)"if"); + ok = append_list(kws, (long long)"int"); + ok = append_list(kws, (long long)"long"); + ok = append_list(kws, (long long)"register"); + ok = append_list(kws, (long long)"return"); + ok = append_list(kws, (long long)"short"); + ok = append_list(kws, (long long)"signed"); + ok = append_list(kws, (long long)"sizeof"); + ok = append_list(kws, (long long)"static"); + ok = append_list(kws, (long long)"struct"); + ok = append_list(kws, (long long)"switch"); + ok = append_list(kws, (long long)"typedef"); + ok = append_list(kws, (long long)"union"); + ok = append_list(kws, (long long)"unsigned"); + ok = append_list(kws, (long long)"void"); + ok = append_list(kws, (long long)"volatile"); + ok = append_list(kws, (long long)"while"); + ok = append_list(kws, (long long)"inline"); + ok = append_list(kws, (long long)"restrict"); + ok = append_list(kws, (long long)"printf"); + ok = append_list(kws, (long long)"scanf"); + ok = append_list(kws, (long long)"malloc"); + ok = append_list(kws, (long long)"free"); + ok = append_list(kws, (long long)"exit"); + ok = append_list(kws, (long long)"read"); + ok = append_list(kws, (long long)"write"); + ok = append_list(kws, (long long)"open"); + ok = append_list(kws, (long long)"close"); + ok = append_list(kws, (long long)"time"); + ok = append_list(kws, (long long)"sleep"); + ok = append_list(kws, (long long)"select"); + ok = append_list(kws, (long long)"remove"); + if (contains_string_val(kws, n) == 1LL) { + ret_val = string_concat((long long)"ep_", n); + goto L_cleanup; + } + ret_val = n; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(2); + free_list(kws); + return ret_val; +} + +long long contains_string_val(long long list, long long s) { + long long key = 0; + long long n = 0; + long long idx = 0; + long long ret_val = 0; + + ep_gc_push_root(&key); + ep_gc_maybe_collect(); + + key = string_concat(s, (long long)""); + n = length_list(list); + idx = 0LL; + while (idx < n) { + if ((strcmp((char*)key, (char*)get_list(list, idx)) == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long get_fn_c_name(long long func) { + long long ret_val = 0; + + ep_gc_maybe_collect(); + + ret_val = cg_sanitize_name(get_list(func, 1LL)); goto L_cleanup; L_cleanup: return ret_val; @@ -12578,7 +12679,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon fk = get_list(state, 3LL); if (map_contains_key(fk, name) == 1LL) { fres = (long long)"(long long)"; - fres = string_concat(fres, name); + fres = string_concat(fres, cg_sanitize_name(name)); ret_val = fres; goto L_cleanup; } @@ -12849,7 +12950,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon goto L_cleanup; } } - call_str = name; + call_str = cg_sanitize_name(name); call_str = string_concat(call_str, (long long)"("); call_str = string_concat(call_str, args_joined); call_str = string_concat(call_str, (long long)")"); diff --git a/ep_codegen.ep b/ep_codegen.ep index e63ceb4..8381cac 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -189,11 +189,80 @@ define string_concat with s1 and s2: set res to string_from_list(lst) return res -define get_fn_c_name with func: - set name to get_list(func and 1) - if "main" equals name: +# Map an ErnosPlain name to a legal, non-colliding C identifier. C keywords and +# common libc clashes are prefixed with ep_ (mirrors the Rust sanitize_c_name); +# `main` maps to `_main`. Must be applied consistently at definitions AND calls. +define cg_sanitize_name with name: + set n to string_concat(name and "") + if "main" equals n: return "_main" - return name + set kws to create_list() + set ok to append_list(kws and "auto") + set ok to append_list(kws and "break") + set ok to append_list(kws and "case") + set ok to append_list(kws and "char") + set ok to append_list(kws and "const") + set ok to append_list(kws and "continue") + set ok to append_list(kws and "default") + set ok to append_list(kws and "do") + set ok to append_list(kws and "double") + set ok to append_list(kws and "else") + set ok to append_list(kws and "enum") + set ok to append_list(kws and "extern") + set ok to append_list(kws and "float") + set ok to append_list(kws and "for") + set ok to append_list(kws and "goto") + set ok to append_list(kws and "if") + set ok to append_list(kws and "int") + set ok to append_list(kws and "long") + set ok to append_list(kws and "register") + set ok to append_list(kws and "return") + set ok to append_list(kws and "short") + set ok to append_list(kws and "signed") + set ok to append_list(kws and "sizeof") + set ok to append_list(kws and "static") + set ok to append_list(kws and "struct") + set ok to append_list(kws and "switch") + set ok to append_list(kws and "typedef") + set ok to append_list(kws and "union") + set ok to append_list(kws and "unsigned") + set ok to append_list(kws and "void") + set ok to append_list(kws and "volatile") + set ok to append_list(kws and "while") + set ok to append_list(kws and "inline") + set ok to append_list(kws and "restrict") + set ok to append_list(kws and "printf") + set ok to append_list(kws and "scanf") + set ok to append_list(kws and "malloc") + set ok to append_list(kws and "free") + set ok to append_list(kws and "exit") + set ok to append_list(kws and "read") + set ok to append_list(kws and "write") + set ok to append_list(kws and "open") + set ok to append_list(kws and "close") + set ok to append_list(kws and "time") + set ok to append_list(kws and "sleep") + set ok to append_list(kws and "select") + set ok to append_list(kws and "remove") + if contains_string_val(kws and n) == 1: + return string_concat("ep_" and n) + return n + +# String membership test. The left operand is coerced with string_concat so the +# codegen emits strcmp, not a pointer comparison (it only string-compares when +# the LEFT operand's type is known to be a string). +define contains_string_val with list and s: + set key to string_concat(s and "") + set n to length_list(list) + set idx to 0 + repeat while idx < n: + if key equals get_list(list and idx): + return 1 + set idx to idx + 1 + return 0 + +define get_fn_c_name with func: + return cg_sanitize_name(get_list(func and 1)) define cg_int_to_str with n: if n == 0: @@ -1423,7 +1492,7 @@ define gen_expr with state and expr and var_keys and var_values: set fk to get_list(state and 3) if map_contains_key(fk and name) == 1: set fres to "(long long)" - set fres to string_concat(fres and name) + set fres to string_concat(fres and cg_sanitize_name(name)) return fres return name @@ -1655,7 +1724,7 @@ define gen_expr with state and expr and var_keys and var_values: set res to string_concat(res and "); })") return res - set call_str to name + set call_str to cg_sanitize_name(name) set call_str to string_concat(call_str and "(") set call_str to string_concat(call_str and args_joined) set call_str to string_concat(call_str and ")") From 1347602d5066b4c592b0b4eafbcf3ddc1ffe0903 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 12:16:47 +0100 Subject: [PATCH 26/51] feat(self-host): check on string/int literals (parity 42->44/54) 'check' assumed every arm was an enum variant and emitted _match_tag == EP_TAG_, so matching on strings/ints (if "red": / if 200:) produced undeclared EP_TAG_red / EP_TAG_200. The parser now records each arm's pattern kind (string 26 / int 25 / variant), and codegen emits strcmp for strings, == for ints, and the tag comparison only for real variants. Gates: FIXPOINT OK; parity 42->44/54 (test_types, test_all_phases); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 23 +++++++++++++++++++++-- ep_codegen.ep | 20 ++++++++++++++++---- ep_parser.ep | 3 +++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 618b8f1..1fca8cf 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -9028,6 +9028,7 @@ long long parse_match_statement(long long state) { long long tok_t = 0; long long variant_tok = 0; long long variant_name = 0; + long long pat_kind = 0; long long bindings = 0; long long next = 0; long long bind_tok = 0; @@ -9060,6 +9061,7 @@ long long parse_match_statement(long long state) { dummy = advance_token(state); variant_tok = advance_token(state); variant_name = get_token_value(variant_tok); + pat_kind = get_token_type(variant_tok); bindings = (create_list() + 0LL); next = peek_token(state); if (get_token_type(next) == 10LL) { @@ -9085,6 +9087,7 @@ long long parse_match_statement(long long state) { ok = append_list(arm_node, variant_name); ok = append_list(arm_node, bindings); ok = append_list(arm_node, arm_body); + ok = append_list(arm_node, pat_kind); ok = append_list(arms, arm_node); } else { dummy = advance_token(state); @@ -12130,6 +12133,7 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon long long vname = 0; long long bindings = 0; long long arm_body = 0; + long long pat_kind = 0; long long kw = 0; long long bname = 0; long long ab_len = 0; @@ -12379,7 +12383,6 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon line = string_concat(line, expr_str); line = string_concat(line, (long long)";\n"); ok = emit(state, line); - ok = emit(state, (long long)" long long _match_tag = ((long long*)_match_val)[0];\n"); arm_len = length_list(arms); arm_idx = 0LL; while (arm_idx < arm_len) { @@ -12387,15 +12390,31 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon vname = get_list(arm, 0LL); bindings = get_list(arm, 1LL); arm_body = get_list(arm, 2LL); + pat_kind = 0LL; + if (length_list(arm) > 3LL) { + pat_kind = get_list(arm, 3LL); + } kw = (long long)"if"; if (arm_idx > 0LL) { kw = (long long)"} else if"; } line = (long long)" "; line = string_concat(line, kw); - line = string_concat(line, (long long)" (_match_tag == EP_TAG_"); + if (pat_kind == 26LL) { + line = string_concat(line, (long long)" (strcmp((char*)_match_val, \""); + line = string_concat(line, escape_string(vname)); + line = string_concat(line, (long long)"\") == 0) {\n"); + } else { + if (pat_kind == 25LL) { + line = string_concat(line, (long long)" (_match_val == "); + line = string_concat(line, vname); + line = string_concat(line, (long long)"LL) {\n"); + } else { + line = string_concat(line, (long long)" (((long long*)_match_val)[0] == EP_TAG_"); line = string_concat(line, vname); line = string_concat(line, (long long)") {\n"); + } + } ok = emit(state, line); b_len = length_list(bindings); b_idx = 0LL; diff --git a/ep_codegen.ep b/ep_codegen.ep index 8381cac..69fbb39 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -1365,7 +1365,6 @@ define gen_statement with state and stmt and var_keys and var_values: set line to string_concat(line and expr_str) set line to string_concat(line and ";\n") set ok to emit(state and line) - set ok to emit(state and " long long _match_tag = ((long long*)_match_val)[0];\n") set arm_len to length_list(arms) set arm_idx to 0 repeat while arm_idx < arm_len: @@ -1373,14 +1372,27 @@ define gen_statement with state and stmt and var_keys and var_values: set vname to get_list(arm and 0) set bindings to get_list(arm and 1) set arm_body to get_list(arm and 2) + set pat_kind to 0 + if length_list(arm) > 3: + set pat_kind to get_list(arm and 3) set kw to "if" if arm_idx > 0: set kw to "} else if" set line to " " set line to string_concat(line and kw) - set line to string_concat(line and " (_match_tag == EP_TAG_") - set line to string_concat(line and vname) - set line to string_concat(line and ") {\n") + if pat_kind == 26: # string literal pattern -> strcmp + set line to string_concat(line and " (strcmp((char*)_match_val, \"") + set line to string_concat(line and escape_string(vname)) + set line to string_concat(line and "\") == 0) {\n") + else: + if pat_kind == 25: # integer literal pattern -> equality + set line to string_concat(line and " (_match_val == ") + set line to string_concat(line and vname) + set line to string_concat(line and "LL) {\n") + else: # enum variant pattern -> tag comparison + set line to string_concat(line and " (((long long*)_match_val)[0] == EP_TAG_") + set line to string_concat(line and vname) + set line to string_concat(line and ") {\n") set ok to emit(state and line) # Extract bindings set b_len to length_list(bindings) diff --git a/ep_parser.ep b/ep_parser.ep index a976c4a..d270ca7 100644 --- a/ep_parser.ep +++ b/ep_parser.ep @@ -1040,6 +1040,8 @@ define parse_match_statement with state: set dummy to advance_token(state) set variant_tok to advance_token(state) set variant_name to get_token_value(variant_tok) + # Pattern kind: 26=string literal, 25=int literal, else variant. + set pat_kind to get_token_type(variant_tok) set bindings to create_list() + 0 set next to peek_token(state) if get_token_type(next) == 10: # with @@ -1062,6 +1064,7 @@ define parse_match_statement with state: set ok to append_list(arm_node and variant_name) set ok to append_list(arm_node and bindings) set ok to append_list(arm_node and arm_body) + set ok to append_list(arm_node and pat_kind) set ok to append_list(arms and arm_node) else: set dummy to advance_token(state) From 3b05540468f21b1f58aea96edf584063fee872e6 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 12:21:18 +0100 Subject: [PATCH 27/51] feat(self-host): resolve all stdlib module imports (sync with Rust driver list) resolve_import_path only knew 11 stdlib modules; add sort/datetime/os/test/log/ sync/regex/csv/websocket/static_server/toml/select/structured so 'import "static_server"' etc. resolve to stdlib/.ep instead of failing relative to the source dir. Gates: FIXPOINT OK; parity 44/54; run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 15 +++++++++++++-- epc.ep | 10 +++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 1fca8cf..7338444 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -5178,6 +5178,8 @@ long long contains_string(long long list, long long s) { } long long resolve_import_path(long long current_file, long long import_path) { + long long p = 0; + long long is_std = 0; long long std_path = 0; long long std_path_ep = 0; long long dir = 0; @@ -5186,13 +5188,22 @@ long long resolve_import_path(long long current_file, long long import_path) { long long ext = 0; long long ret_val = 0; + ep_gc_push_root(&p); ep_gc_push_root(&std_path); ep_gc_push_root(&std_path_ep); ep_gc_push_root(&resolved); ep_gc_push_root(&ext); ep_gc_maybe_collect(); - if ((((((((((((strcmp((char*)(long long)"math", (char*)import_path) == 0) || (strcmp((char*)(long long)"hash", (char*)import_path) == 0)) || (strcmp((char*)(long long)"net", (char*)import_path) == 0)) || (strcmp((char*)(long long)"json", (char*)import_path) == 0)) || (strcmp((char*)(long long)"string", (char*)import_path) == 0)) || (strcmp((char*)(long long)"sql", (char*)import_path) == 0)) || (strcmp((char*)(long long)"gui", (char*)import_path) == 0)) || (strcmp((char*)(long long)"crypto", (char*)import_path) == 0)) || (strcmp((char*)(long long)"fs", (char*)import_path) == 0)) || (strcmp((char*)(long long)"http", (char*)import_path) == 0)) || (strcmp((char*)(long long)"collections", (char*)import_path) == 0))) { + p = string_concat(import_path, (long long)""); + is_std = 0LL; + if ((((((((((((strcmp((char*)(long long)"math", (char*)p) == 0) || (strcmp((char*)(long long)"hash", (char*)p) == 0)) || (strcmp((char*)(long long)"net", (char*)p) == 0)) || (strcmp((char*)(long long)"json", (char*)p) == 0)) || (strcmp((char*)(long long)"string", (char*)p) == 0)) || (strcmp((char*)(long long)"sql", (char*)p) == 0)) || (strcmp((char*)(long long)"gui", (char*)p) == 0)) || (strcmp((char*)(long long)"crypto", (char*)p) == 0)) || (strcmp((char*)(long long)"fs", (char*)p) == 0)) || (strcmp((char*)(long long)"http", (char*)p) == 0)) || (strcmp((char*)(long long)"collections", (char*)p) == 0))) { + is_std = 1LL; + } + if ((((((((((((((strcmp((char*)(long long)"sort", (char*)p) == 0) || (strcmp((char*)(long long)"datetime", (char*)p) == 0)) || (strcmp((char*)(long long)"os", (char*)p) == 0)) || (strcmp((char*)(long long)"test", (char*)p) == 0)) || (strcmp((char*)(long long)"log", (char*)p) == 0)) || (strcmp((char*)(long long)"sync", (char*)p) == 0)) || (strcmp((char*)(long long)"regex", (char*)p) == 0)) || (strcmp((char*)(long long)"csv", (char*)p) == 0)) || (strcmp((char*)(long long)"websocket", (char*)p) == 0)) || (strcmp((char*)(long long)"static_server", (char*)p) == 0)) || (strcmp((char*)(long long)"toml", (char*)p) == 0)) || (strcmp((char*)(long long)"select", (char*)p) == 0)) || (strcmp((char*)(long long)"structured", (char*)p) == 0))) { + is_std = 1LL; + } + if (is_std == 1LL) { std_path = string_concat((long long)"stdlib/", import_path); std_path_ep = string_concat(std_path, (long long)".ep"); ret_val = std_path_ep; @@ -5211,7 +5222,7 @@ long long resolve_import_path(long long current_file, long long import_path) { ret_val = string_concat(resolved, (long long)".ep"); goto L_cleanup; L_cleanup: - ep_gc_pop_roots(4); + ep_gc_pop_roots(5); return ret_val; } diff --git a/epc.ep b/epc.ep index 8c84764..d726d77 100644 --- a/epc.ep +++ b/epc.ep @@ -54,7 +54,15 @@ define contains_string with list and s: return 0 define resolve_import_path with current_file and import_path: - if "math" equals import_path || "hash" equals import_path || "net" equals import_path || "json" equals import_path || "string" equals import_path || "sql" equals import_path || "gui" equals import_path || "crypto" equals import_path || "fs" equals import_path || "http" equals import_path || "collections" equals import_path: + # Bare stdlib module names resolve to stdlib/.ep (matches the Rust + # driver's stdlib_modules list). + set p to string_concat(import_path and "") + set is_std to 0 + if "math" equals p || "hash" equals p || "net" equals p || "json" equals p || "string" equals p || "sql" equals p || "gui" equals p || "crypto" equals p || "fs" equals p || "http" equals p || "collections" equals p: + set is_std to 1 + if "sort" equals p || "datetime" equals p || "os" equals p || "test" equals p || "log" equals p || "sync" equals p || "regex" equals p || "csv" equals p || "websocket" equals p || "static_server" equals p || "toml" equals p || "select" equals p || "structured" equals p: + set is_std to 1 + if is_std == 1: set std_path to string_concat("stdlib/" and import_path) set std_path_ep to string_concat(std_path and ".ep") return std_path_ep From 71f535c11020d7604ff1b97b74d379172db35de5 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 12:28:38 +0100 Subject: [PATCH 28/51] feat(self-host): full English comparison-phrase aliases (parity 44->45/54) Add the remaining 'is ...' comparison phrases (is more/fewer/smaller/bigger/ larger than, is at least/most, is different from, is the same as) plus 'does not equal', mirroring src/lexer.rs. New lex_is_phrase2 helper. Gates: FIXPOINT OK; parity 44->45/54 (test_english_aliases); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 131 ++++++++++++++++++++++++++++++++++++++ ep_lexer.ep | 97 ++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 7338444..0955625 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -4903,6 +4903,7 @@ long long get_token_line(long long); long long get_token_col(long long); long long match_next_word(long long, long long, long long); long long lex_string_body(long long, long long, long long, long long, long long, long long, long long); +long long lex_is_phrase2(long long, long long, long long, long long); long long tokenize_source(long long); long long parse_int(long long); long long make_node_int(long long); @@ -6183,6 +6184,25 @@ long long lex_string_body(long long source, long long pos0, long long source_len return ret_val; } +long long lex_is_phrase2(long long source, long long pos, long long w1, long long w2) { + long long p1 = 0; + long long p2 = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + p1 = match_next_word(source, pos, w1); + if (p1 == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + p2 = match_next_word(source, p1, w2); + ret_val = p2; + goto L_cleanup; +L_cleanup: + return ret_val; +} + long long tokenize_source(long long source) { long long tokens = 0; long long source_len = 0; @@ -6224,6 +6244,11 @@ long long tokenize_source(long long source) { long long next_p = 0; long long next_p2 = 0; long long next_p3 = 0; + long long mp2 = 0; + long long mp_the = 0; + long long mp3 = 0; + long long dn = 0; + long long de = 0; long long start_col = 0; long long is_fstring = 0; long long tok_count = 0; @@ -6459,10 +6484,116 @@ long long tokenize_source(long long source) { } } if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"more", (long long)"than"); + if (mp2 > 0LL) { + tok_type = 17LL; + id_str = (long long)">"; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"fewer", (long long)"than"); + if (mp2 > 0LL) { + tok_type = 16LL; + id_str = (long long)"<"; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"smaller", (long long)"than"); + if (mp2 > 0LL) { + tok_type = 16LL; + id_str = (long long)"<"; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"bigger", (long long)"than"); + if (mp2 > 0LL) { + tok_type = 17LL; + id_str = (long long)">"; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"larger", (long long)"than"); + if (mp2 > 0LL) { + tok_type = 17LL; + id_str = (long long)">"; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"at", (long long)"least"); + if (mp2 > 0LL) { + tok_type = 68LL; + id_str = (long long)">="; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"at", (long long)"most"); + if (mp2 > 0LL) { + tok_type = 67LL; + id_str = (long long)"<="; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp2 = lex_is_phrase2(source, pos, (long long)"different", (long long)"from"); + if (mp2 > 0LL) { + tok_type = 19LL; + id_str = (long long)"!="; + current_col = (current_col + (mp2 - pos)); + pos = mp2; + is_multi_phrase = 1LL; + } + } + if (is_multi_phrase == 0LL) { + mp_the = match_next_word(source, pos, (long long)"the"); + if (mp_the > 0LL) { + mp3 = lex_is_phrase2(source, mp_the, (long long)"same", (long long)"as"); + if (mp3 > 0LL) { + tok_type = 18LL; + id_str = (long long)"=="; + current_col = (current_col + (mp3 - pos)); + pos = mp3; + is_multi_phrase = 1LL; + } + } + } + if (is_multi_phrase == 0LL) { tok_type = 72LL; is_multi_phrase = 1LL; } } + if ((strcmp((char*)(long long)"does", (char*)id_str) == 0)) { + dn = match_next_word(source, pos, (long long)"not"); + if (dn > 0LL) { + de = match_next_word(source, dn, (long long)"equal"); + if (de > 0LL) { + tok_type = 19LL; + id_str = (long long)"!="; + current_col = (current_col + (de - pos)); + pos = de; + is_multi_phrase = 1LL; + } + } + } } if (is_multi_phrase == 0LL) { if ((strcmp((char*)(long long)"and", (char*)id_str) == 0)) { diff --git a/ep_lexer.ep b/ep_lexer.ep index 472e934..9dd7385 100644 --- a/ep_lexer.ep +++ b/ep_lexer.ep @@ -234,6 +234,15 @@ define lex_string_body with source and pos0 and source_len and tokens and curren set okr3 to append_list(res and err) return res +# Match two consecutive words `w1 w2` starting at `pos`; returns the position +# after w2 (or 0). Used for `is ` comparison phrases. +define lex_is_phrase2 with source and pos and w1 and w2: + set p1 to match_next_word(source and pos and w1) + if p1 == 0: + return 0 + set p2 to match_next_word(source and p1 and w2) + return p2 + define tokenize_source with source: set tokens to create_list() set source_len to string_length(source) @@ -440,10 +449,98 @@ define tokenize_source with source: set pos to next_p2 set is_multi_phrase to 1 + # Two-word comparison phrases (mirror src/lexer.rs) + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "more" and "than") + if mp2 > 0: + set tok_type to 17 + set id_str to ">" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "fewer" and "than") + if mp2 > 0: + set tok_type to 16 + set id_str to "<" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "smaller" and "than") + if mp2 > 0: + set tok_type to 16 + set id_str to "<" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "bigger" and "than") + if mp2 > 0: + set tok_type to 17 + set id_str to ">" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "larger" and "than") + if mp2 > 0: + set tok_type to 17 + set id_str to ">" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "at" and "least") + if mp2 > 0: + set tok_type to 68 + set id_str to ">=" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "at" and "most") + if mp2 > 0: + set tok_type to 67 + set id_str to "<=" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + set mp2 to lex_is_phrase2(source and pos and "different" and "from") + if mp2 > 0: + set tok_type to 19 + set id_str to "!=" + set current_col to current_col + (mp2 - pos) + set pos to mp2 + set is_multi_phrase to 1 + if is_multi_phrase == 0: + # `is the same as` -> == (three words) + set mp_the to match_next_word(source and pos and "the") + if mp_the > 0: + set mp3 to lex_is_phrase2(source and mp_the and "same" and "as") + if mp3 > 0: + set tok_type to 18 + set id_str to "==" + set current_col to current_col + (mp3 - pos) + set pos to mp3 + set is_multi_phrase to 1 + if is_multi_phrase == 0: # Standalone 'is' for struct field defaults set tok_type to 72 set is_multi_phrase to 1 + if "does" equals id_str: + # `does not equal` -> != + set dn to match_next_word(source and pos and "not") + if dn > 0: + set de to match_next_word(source and dn and "equal") + if de > 0: + set tok_type to 19 + set id_str to "!=" + set current_col to current_col + (de - pos) + set pos to de + set is_multi_phrase to 1 if is_multi_phrase == 0: if "and" equals id_str: From 9719ab2c1a676eb2718e5be6ae59f84a01242690 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 12:55:24 +0100 Subject: [PATCH 29/51] feat(self-host): emit trait-implementation methods (parity 45->46/54) Trait impls (program index 8) were parsed but never codegen'd, so p.size() hit 'call to undeclared function size'. Emit each impl's methods as functions with self prepended (mirroring regular method_defs), dispatched by bare name. Deduplicated across impls so a method name shared by multiple types (e.g. to_string for Point and Shape) is emitted once rather than colliding. Gates: FIXPOINT OK; parity 45->46/54 (test_traits); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 61 ++++++++++++++++++++++++++++++++++++++- ep_codegen.ep | 39 ++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 0955625..75fbb4a 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -14586,6 +14586,23 @@ long long generate_c(long long program, long long is_test_mode) { long long mp_idx = 0; long long mp = 0; long long method_func = 0; + long long emitted_methods = 0; + long long trait_impls = 0; + long long ti_len = 0; + long long ti_idx = 0; + long long timpl = 0; + long long imethods = 0; + long long im_len = 0; + long long im_idx = 0; + long long imeth = 0; + long long imeth_name = 0; + long long tparams = 0; + long long tbody = 0; + long long tfull = 0; + long long tself = 0; + long long tp_len = 0; + long long tp_idx = 0; + long long tfunc = 0; long long lines = 0; long long cbodies = 0; long long marker_line = 0; @@ -14611,6 +14628,7 @@ long long generate_c(long long program, long long is_test_mode) { ep_gc_push_root(&spawn_list); ep_gc_push_root(&struct_decl); ep_gc_push_root(&wrap_fn); + ep_gc_push_root(&emitted_methods); ep_gc_push_root(&spliced); ep_gc_push_root(&c_code); ep_gc_maybe_collect(); @@ -14946,6 +14964,46 @@ long long generate_c(long long program, long long is_test_mode) { md_idx = (md_idx + 1LL); } } + { + long long tmp_val = create_list(); + free_list(emitted_methods); + emitted_methods = tmp_val; + } + if (prog_len > 8LL) { + trait_impls = get_list(program, 8LL); + ti_len = length_list(trait_impls); + ti_idx = 0LL; + while (ti_idx < ti_len) { + timpl = get_list(trait_impls, ti_idx); + imethods = get_list(timpl, 3LL); + im_len = length_list(imethods); + im_idx = 0LL; + while (im_idx < im_len) { + imeth = get_list(imethods, im_idx); + imeth_name = get_list(imeth, 1LL); + if (contains_string_val(emitted_methods, imeth_name) == 0LL) { + ok = append_list(emitted_methods, imeth_name); + tparams = get_list(imeth, 2LL); + tbody = get_list(imeth, 3LL); + tfull = (create_list() + 0LL); + tself = (create_list() + 0LL); + ok = append_list(tself, (long long)"self"); + ok = append_list(tself, 0LL); + ok = append_list(tfull, tself); + tp_len = length_list(tparams); + tp_idx = 0LL; + while (tp_idx < tp_len) { + ok = append_list(tfull, get_list(tparams, tp_idx)); + tp_idx = (tp_idx + 1LL); + } + tfunc = (make_node_func(imeth_name, tfull, tbody, 0LL) + 0LL); + ok = gen_function(state, tfunc); + } + im_idx = (im_idx + 1LL); + } + ti_idx = (ti_idx + 1LL); + } + } idx = 0LL; while (idx < len) { func = get_list(funcs, idx); @@ -14970,13 +15028,14 @@ long long generate_c(long long program, long long is_test_mode) { ret_val = c_code; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(19); + ep_gc_pop_roots(20); free_list(state); free_list(field_slots); free_list(tag_slots); free_list(gk); free_list(gv); free_list(spawn_list); + free_list(emitted_methods); return ret_val; } diff --git a/ep_codegen.ep b/ep_codegen.ep index 69fbb39..a65b772 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -2938,7 +2938,44 @@ define generate_c with program and is_test_mode: set method_func to make_node_func(mname and full_params and mbody and 0) + 0 set ok to gen_function(state and method_func) set md_idx to md_idx + 1 - + + # Emit trait-implementation methods (index 8). Each impl is + # [41, trait_name, type_name, methods]; methods are function nodes. They are + # dispatched by bare method name (p.size() -> size(p)), so emit each distinct + # method name once — deduplicated across impls. + set emitted_methods to create_list() + if prog_len > 8: + set trait_impls to get_list(program and 8) + set ti_len to length_list(trait_impls) + set ti_idx to 0 + repeat while ti_idx < ti_len: + set timpl to get_list(trait_impls and ti_idx) + set imethods to get_list(timpl and 3) + set im_len to length_list(imethods) + set im_idx to 0 + repeat while im_idx < im_len: + set imeth to get_list(imethods and im_idx) + set imeth_name to get_list(imeth and 1) + if contains_string_val(emitted_methods and imeth_name) == 0: + set ok to append_list(emitted_methods and imeth_name) + # Build a function with self prepended to the method's params. + set tparams to get_list(imeth and 2) + set tbody to get_list(imeth and 3) + set tfull to create_list() + 0 + set tself to create_list() + 0 + set ok to append_list(tself and "self") + set ok to append_list(tself and 0) + set ok to append_list(tfull and tself) + set tp_len to length_list(tparams) + set tp_idx to 0 + repeat while tp_idx < tp_len: + set ok to append_list(tfull and get_list(tparams and tp_idx)) + set tp_idx to tp_idx + 1 + set tfunc to make_node_func(imeth_name and tfull and tbody and 0) + 0 + set ok to gen_function(state and tfunc) + set im_idx to im_idx + 1 + set ti_idx to ti_idx + 1 + # Emit functions (skipping any that shadow a C-runtime builtin — the # builtin definition wins, mirroring the Rust compiler) set idx to 0 From 3befd06cac38cb7773ab263b85ecb4b914f335bd Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 13:00:24 +0100 Subject: [PATCH 30/51] fix(self-host): closures and spawn share a counter (parity 46->47/54) Closures reused spawn_index for their _ep_closure_N names, so a closure defined before a spawn desynced the spawn arg-struct index (spawn_args_1 referenced but spawn_args_0 defined). Give closures a dedicated counter (state slot 13). Gates: FIXPOINT OK; parity 46->47/54 (test_closure_spawn); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 5 +++-- ep_codegen.ep | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 75fbb4a..45135e9 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -11002,6 +11002,7 @@ long long create_codegen_state() { ok = append_list(state, 0LL); ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, 0LL); ret_val = state; state = 0; goto L_cleanup; @@ -13259,8 +13260,8 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon if (type == 34LL) { params = get_list(expr, 1LL); body = get_list(expr, 2LL); - cidx = get_codegen_spawn_index(state); - dummy = set_codegen_spawn_index(state, (cidx + 1LL)); + cidx = get_list(state, 13LL); + dummy = set_list(state, 13LL, (cidx + 1LL)); cname = string_concat((long long)"_ep_closure_", cg_int_to_str(cidx)); { long long tmp_val = create_list(); diff --git a/ep_codegen.ep b/ep_codegen.ep index a65b772..430af61 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -356,6 +356,7 @@ define create_codegen_state: set ok to append_list(state and 0) # builtin_count (10): entries in func_keys[0..n) that are C-runtime builtins set ok to append_list(state and (create_list() + 0)) # closure_bodies (11): lifted closure C functions, spliced at the marker set ok to append_list(state and (create_list() + 0)) # enum_variant_names (12): every declared variant, for bare-variant identifiers + set ok to append_list(state and 0) # closure_index (13): separate counter so closures don't perturb spawn_index return state # A user/imported .ep function whose name matches a C-runtime builtin must NOT @@ -1871,8 +1872,10 @@ define gen_expr with state and expr and var_keys and var_values: # mallocs {magic, fn_ptr, env[]} and packs current capture values. set params to get_list(expr and 1) set body to get_list(expr and 2) - set cidx to get_codegen_spawn_index(state) - set dummy to set_codegen_spawn_index(state and cidx + 1) + # Dedicated closure counter (slot 13): sharing spawn_index desynced the + # spawn arg-struct indices when closures and spawn coexist. + set cidx to get_list(state and 13) + set dummy to set_list(state and 13 and cidx + 1) set cname to string_concat("_ep_closure_" and cg_int_to_str(cidx)) # ---- capture analysis: identifiers read in the body that are outer From fc0f11452ae932195bad32ecc3f19406dcb4089e Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 13:06:54 +0100 Subject: [PATCH 31/51] feat(self-host): try/Result unwrapping + typed check bindings (parity 47->48/54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Parser captures function return-type annotations (func node index 5). - codegen builds function->return-enum-Ok-variant and variant->field-type maps. - 'try f(...)' where f returns an enum now unwraps: non-Ok tag propagates (ret_val = enum; goto L_cleanup), else yields the Ok payload — mirroring the Rust compiler's first-variant-is-Ok convention. - 'check' arm bindings are now typed from the variant's declared field types, so an Error(message as Str) payload displays as %s, not the raw pointer. Gates: FIXPOINT OK; parity 47->48/54 (test_errors); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 206 +++++++++++++++++++++++++++++++++++++- ep_codegen.ep | 118 +++++++++++++++++++++- ep_parser.ep | 22 ++-- 3 files changed, 332 insertions(+), 14 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 45135e9..7663da4 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -5001,6 +5001,7 @@ long long cg_int_to_str(long long); long long escape_string(long long); long long join_strings(long long); long long create_codegen_state(); +long long type_name_to_code(long long); long long is_builtin_c_func(long long, long long); long long get_codegen_borrowed_keys(long long); long long set_codegen_borrowed_keys(long long, long long); @@ -8886,10 +8887,13 @@ long long parse_function_async(long long state, long long is_async) { long long tok_name = 0; long long name = 0; long long params = 0; + long long ret_type = 0; long long next_ret = 0; long long dummy = 0; + long long ret_tok = 0; long long tok_nl = 0; long long body = 0; + long long fnode = 0; long long ret_val = 0; ep_gc_maybe_collect(); @@ -8898,10 +8902,12 @@ long long parse_function_async(long long state, long long is_async) { tok_name = advance_token(state); name = get_token_value(tok_name); params = (parse_param_list(state) + 0LL); + ret_type = (long long)""; next_ret = peek_token(state); if (get_token_type(next_ret) == 43LL) { dummy = advance_token(state); - dummy = advance_token(state); + ret_tok = advance_token(state); + ret_type = get_token_value(ret_tok); } ok = expect_token_type(state, 22LL); tok_nl = peek_token(state); @@ -8909,7 +8915,9 @@ long long parse_function_async(long long state, long long is_async) { dummy = advance_token(state); } body = (parse_block(state) + 0LL); - ret_val = make_node_func(name, params, body, is_async); + fnode = (make_node_func(name, params, body, is_async) + 0LL); + ok = append_list(fnode, ret_type); + ret_val = fnode; goto L_cleanup; L_cleanup: return ret_val; @@ -11003,6 +11011,10 @@ long long create_codegen_state() { ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, 0LL); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); ret_val = state; state = 0; goto L_cleanup; @@ -11012,6 +11024,33 @@ long long create_codegen_state() { return ret_val; } +long long type_name_to_code(long long tname) { + long long t = 0; + long long ret_val = 0; + + ep_gc_push_root(&t); + ep_gc_maybe_collect(); + + t = string_concat(tname, (long long)""); + if ((strcmp((char*)(long long)"Str", (char*)t) == 0)) { + ret_val = 2LL; + goto L_cleanup; + } + if ((strcmp((char*)(long long)"Float", (char*)t) == 0)) { + ret_val = 8LL; + goto L_cleanup; + } + if ((strcmp((char*)(long long)"Bool", (char*)t) == 0)) { + ret_val = 7LL; + goto L_cleanup; + } + ret_val = 1LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + long long is_builtin_c_func(long long state, long long name) { long long name_str = 0; long long keys = 0; @@ -12278,7 +12317,13 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon long long arm_body = 0; long long pat_kind = 0; long long kw = 0; + long long vp_codes = 0; + long long vpk = 0; + long long vpv = 0; + long long vpk_i = 0; + long long vpk_len = 0; long long bname = 0; + long long bcode = 0; long long ab_len = 0; long long ab_idx = 0; long long var_name = 0; @@ -12560,6 +12605,17 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon } ok = emit(state, line); b_len = length_list(bindings); + vp_codes = 0LL; + vpk = get_list(state, 16LL); + vpv = get_list(state, 17LL); + vpk_i = 0LL; + vpk_len = length_list(vpk); + while (vpk_i < vpk_len) { + if ((strcmp((char*)string_concat(vname, (long long)""), (char*)get_list(vpk, vpk_i)) == 0)) { + vp_codes = get_list(vpv, vpk_i); + } + vpk_i = (vpk_i + 1LL); + } b_idx = 0LL; while (b_idx < b_len) { bname = get_list(bindings, b_idx); @@ -12569,6 +12625,13 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon line = string_concat(line, cg_int_to_str((b_idx + 1LL))); line = string_concat(line, (long long)"];\n"); ok = emit(state, line); + bcode = 1LL; + if (vp_codes != 0LL) { + if (b_idx < length_list(vp_codes)) { + bcode = get_list(vp_codes, b_idx); + } + } + ok = map_put(var_keys, var_values, bname, bcode); b_idx = (b_idx + 1LL); } ab_len = length_list(arm_body); @@ -12705,10 +12768,19 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon long long variant_name = 0; long long alloc_size = 0; long long a_idx = 0; + long long ok_variant = 0; + long long fkeys = 0; + long long fvals = 0; + long long callee = 0; + long long fi = 0; + long long fn = 0; + long long tid = 0; + long long dummy = 0; + long long tv = 0; + long long r = 0; long long params = 0; long long body = 0; long long cidx = 0; - long long dummy = 0; long long cname = 0; long long raw_names = 0; long long okr = 0; @@ -12787,6 +12859,8 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon ep_gc_push_root(&obj_str); ep_gc_push_root(&fval_str); ep_gc_push_root(&arg_str); + ep_gc_push_root(&tv); + ep_gc_push_root(&r); ep_gc_push_root(&cname); ep_gc_push_root(&raw_names); ep_gc_push_root(&captured); @@ -13254,9 +13328,43 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon if (type == 33LL) { inner = get_list(expr, 1LL); inner_str = gen_expr(state, inner, var_keys, var_values); + ok_variant = (long long)""; + if (get_list(inner, 0LL) == 6LL) { + fkeys = get_list(state, 14LL); + fvals = get_list(state, 15LL); + callee = get_list(inner, 1LL); + fi = 0LL; + fn = length_list(fkeys); + while (fi < fn) { + if ((strcmp((char*)string_concat(callee, (long long)""), (char*)get_list(fkeys, fi)) == 0)) { + ok_variant = get_list(fvals, fi); + } + fi = (fi + 1LL); + } + } + if (string_length((char*)ok_variant) == 0LL) { ret_val = inner_str; goto L_cleanup; } + tid = get_list(state, 13LL); + dummy = set_list(state, 13LL, (tid + 1LL)); + tv = string_concat((long long)"_try", cg_int_to_str(tid)); + r = (long long)"({ long long "; + r = string_concat(r, tv); + r = string_concat(r, (long long)" = "); + r = string_concat(r, inner_str); + r = string_concat(r, (long long)"; if (((long long*)"); + r = string_concat(r, tv); + r = string_concat(r, (long long)")[0] != EP_TAG_"); + r = string_concat(r, ok_variant); + r = string_concat(r, (long long)") { ret_val = "); + r = string_concat(r, tv); + r = string_concat(r, (long long)"; goto L_cleanup; } ((long long*)"); + r = string_concat(r, tv); + r = string_concat(r, (long long)")[1]; })"); + ret_val = r; + goto L_cleanup; + } if (type == 34LL) { params = get_list(expr, 1LL); body = get_list(expr, 2LL); @@ -13460,7 +13568,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon ret_val = (long long)""; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(36); + ep_gc_pop_roots(38); free_list(formatted_args); free_list(args_str_list); free_list(raw_names); @@ -14534,6 +14642,30 @@ long long generate_c(long long program, long long is_test_mode) { long long ts_len = 0; long long ts_idx = 0; long long tname = 0; + long long vpat_keys = 0; + long long vpat_vals = 0; + long long vp_enums = 0; + long long vp_i = 0; + long long vp_len = 0; + long long vp_variants = 0; + long long vpv_i = 0; + long long vpv_len = 0; + long long vp_var = 0; + long long vp_fields = 0; + long long vp_codes = 0; + long long vpf_i = 0; + long long vpf_len = 0; + long long try_keys = 0; + long long try_vals = 0; + long long all_enums = 0; + long long try_funcs = 0; + long long tf_len = 0; + long long tf_i = 0; + long long tf = 0; + long long rt = 0; + long long en_i = 0; + long long en_len = 0; + long long evs = 0; long long constants = 0; long long const_n = 0; long long ci = 0; @@ -14617,6 +14749,8 @@ long long generate_c(long long program, long long is_test_mode) { ep_gc_push_root(&line); ep_gc_push_root(&slot_line); ep_gc_push_root(&tag_slots); + ep_gc_push_root(&vp_codes); + ep_gc_push_root(&rt); ep_gc_push_root(&gline); ep_gc_push_root(&ml); ep_gc_push_root(&gk); @@ -14728,6 +14862,67 @@ long long generate_c(long long program, long long is_test_mode) { } } } + vpat_keys = get_list(state, 16LL); + vpat_vals = get_list(state, 17LL); + if (prog_len > 5LL) { + vp_enums = get_list(program, 5LL); + vp_i = 0LL; + vp_len = length_list(vp_enums); + while (vp_i < vp_len) { + vp_variants = get_list(get_list(vp_enums, vp_i), 2LL); + vpv_i = 0LL; + vpv_len = length_list(vp_variants); + while (vpv_i < vpv_len) { + vp_var = get_list(vp_variants, vpv_i); + vp_fields = get_list(vp_var, 1LL); + { + long long tmp_val = create_list(); + free_list(vp_codes); + vp_codes = tmp_val; + } + vpf_i = 0LL; + vpf_len = length_list(vp_fields); + while (vpf_i < vpf_len) { + ok = append_list(vp_codes, type_name_to_code(get_list(get_list(vp_fields, vpf_i), 1LL))); + vpf_i = (vpf_i + 1LL); + } + ok = append_list(vpat_keys, get_list(vp_var, 0LL)); + ok = append_list(vpat_vals, vp_codes); + vpv_i = (vpv_i + 1LL); + } + vp_i = (vp_i + 1LL); + } + } + try_keys = get_list(state, 14LL); + try_vals = get_list(state, 15LL); + if (prog_len > 5LL) { + all_enums = get_list(program, 5LL); + try_funcs = get_list(program, 3LL); + tf_len = length_list(try_funcs); + tf_i = 0LL; + while (tf_i < tf_len) { + tf = get_list(try_funcs, tf_i); + if (length_list(tf) > 5LL) { + rt = string_concat(get_list(tf, 5LL), (long long)""); + if (string_length((char*)rt) > 0LL) { + en_i = 0LL; + en_len = length_list(all_enums); + while (en_i < en_len) { + edef = get_list(all_enums, en_i); + if ((strcmp((char*)rt, (char*)get_list(edef, 1LL)) == 0)) { + evs = get_list(edef, 2LL); + if (length_list(evs) > 0LL) { + ok = append_list(try_keys, get_list(tf, 1LL)); + ok = append_list(try_vals, get_list(get_list(evs, 0LL), 0LL)); + } + } + en_i = (en_i + 1LL); + } + } + } + tf_i = (tf_i + 1LL); + } + } constants = create_list(); if (length_list(program) > 9LL) { constants = get_list(program, 9LL); @@ -15029,10 +15224,11 @@ long long generate_c(long long program, long long is_test_mode) { ret_val = c_code; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(20); + ep_gc_pop_roots(22); free_list(state); free_list(field_slots); free_list(tag_slots); + free_list(vp_codes); free_list(gk); free_list(gv); free_list(spawn_list); diff --git a/ep_codegen.ep b/ep_codegen.ep index 430af61..ad59537 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -357,8 +357,23 @@ define create_codegen_state: set ok to append_list(state and (create_list() + 0)) # closure_bodies (11): lifted closure C functions, spliced at the marker set ok to append_list(state and (create_list() + 0)) # enum_variant_names (12): every declared variant, for bare-variant identifiers set ok to append_list(state and 0) # closure_index (13): separate counter so closures don't perturb spawn_index + set ok to append_list(state and (create_list() + 0)) # try_ok_keys (14): function names returning an enum + set ok to append_list(state and (create_list() + 0)) # try_ok_vals (15): that enum's first (Ok) variant name + set ok to append_list(state and (create_list() + 0)) # vpat_keys (16): variant name + set ok to append_list(state and (create_list() + 0)) # vpat_vals (17): list of field type-codes for that variant return state +# Map an ErnosPlain type-annotation name to the codegen's coarse type code. +define type_name_to_code with tname: + set t to string_concat(tname and "") + if "Str" equals t: + return 2 + if "Float" equals t: + return 8 + if "Bool" equals t: + return 7 + return 1 + # A user/imported .ep function whose name matches a C-runtime builtin must NOT # be emitted — the C builtin wins (mirrors the Rust compiler's builtin_c_funcs # skip). Builtins occupy the first builtin_count slots of func_keys. @@ -1397,6 +1412,16 @@ define gen_statement with state and stmt and var_keys and var_values: set ok to emit(state and line) # Extract bindings set b_len to length_list(bindings) + # Look up this variant's field type-codes so bindings are typed. + set vp_codes to 0 + set vpk to get_list(state and 16) + set vpv to get_list(state and 17) + set vpk_i to 0 + set vpk_len to length_list(vpk) + repeat while vpk_i < vpk_len: + if string_concat(vname and "") equals get_list(vpk and vpk_i): + set vp_codes to get_list(vpv and vpk_i) + set vpk_i to vpk_i + 1 set b_idx to 0 repeat while b_idx < b_len: set bname to get_list(bindings and b_idx) @@ -1406,6 +1431,12 @@ define gen_statement with state and stmt and var_keys and var_values: set line to string_concat(line and cg_int_to_str(b_idx + 1)) set line to string_concat(line and "];\n") set ok to emit(state and line) + # Register the binding's type so display/uses format correctly. + set bcode to 1 + if vp_codes != 0: + if b_idx < length_list(vp_codes): + set bcode to get_list(vp_codes and b_idx) + set ok to map_put(var_keys and var_values and bname and bcode) set b_idx to b_idx + 1 # Emit arm body set ab_len to length_list(arm_body) @@ -1863,7 +1894,40 @@ define gen_expr with state and expr and var_keys and var_values: if type == 33: # NODE_TRY set inner to get_list(expr and 1) set inner_str to gen_expr(state and inner and var_keys and var_values) - return inner_str + # If the inner expression is a call to a function returning a Result-style + # enum, unwrap it: on a non-Ok tag, propagate (ret_val = enum; goto + # L_cleanup); otherwise yield the Ok payload (_v[1]). Convention: first + # variant is the success/Ok variant (mirrors the Rust compiler). + set ok_variant to "" + if get_list(inner and 0) == 6: # NODE_CALL + set fkeys to get_list(state and 14) + set fvals to get_list(state and 15) + set callee to get_list(inner and 1) + set fi to 0 + set fn to length_list(fkeys) + repeat while fi < fn: + if string_concat(callee and "") equals get_list(fkeys and fi): + set ok_variant to get_list(fvals and fi) + set fi to fi + 1 + if string_length(ok_variant) == 0: + return inner_str + set tid to get_list(state and 13) + set dummy to set_list(state and 13 and tid + 1) + set tv to string_concat("_try" and cg_int_to_str(tid)) + set r to "({ long long " + set r to string_concat(r and tv) + set r to string_concat(r and " = ") + set r to string_concat(r and inner_str) + set r to string_concat(r and "; if (((long long*)") + set r to string_concat(r and tv) + set r to string_concat(r and ")[0] != EP_TAG_") + set r to string_concat(r and ok_variant) + set r to string_concat(r and ") { ret_val = ") + set r to string_concat(r and tv) + set r to string_concat(r and "; goto L_cleanup; } ((long long*)") + set r to string_concat(r and tv) + set r to string_concat(r and ")[1]; })") + return r if type == 34: # NODE_CLOSURE # Mirrors the Rust compiler's EpClosure ABI: @@ -2709,6 +2773,58 @@ define generate_c with program and is_test_mode: set ok to emit(state and line) set ts_idx to ts_idx + 1 + # Build the variant -> field-type-codes map so `check` bindings get typed + # (e.g. an Error(message as Str) payload displays as %s, not %lld). + set vpat_keys to get_list(state and 16) + set vpat_vals to get_list(state and 17) + if prog_len > 5: + set vp_enums to get_list(program and 5) + set vp_i to 0 + set vp_len to length_list(vp_enums) + repeat while vp_i < vp_len: + set vp_variants to get_list(get_list(vp_enums and vp_i) and 2) + set vpv_i to 0 + set vpv_len to length_list(vp_variants) + repeat while vpv_i < vpv_len: + set vp_var to get_list(vp_variants and vpv_i) + set vp_fields to get_list(vp_var and 1) + set vp_codes to create_list() + set vpf_i to 0 + set vpf_len to length_list(vp_fields) + repeat while vpf_i < vpf_len: + set ok to append_list(vp_codes and type_name_to_code(get_list(get_list(vp_fields and vpf_i) and 1))) + set vpf_i to vpf_i + 1 + set ok to append_list(vpat_keys and get_list(vp_var and 0)) + set ok to append_list(vpat_vals and vp_codes) + set vpv_i to vpv_i + 1 + set vp_i to vp_i + 1 + + # Build the try-unwrap map: function name -> its return enum's first (Ok) + # variant, for every function whose declared return type is an enum. + set try_keys to get_list(state and 14) + set try_vals to get_list(state and 15) + if prog_len > 5: + set all_enums to get_list(program and 5) + set try_funcs to get_list(program and 3) + set tf_len to length_list(try_funcs) + set tf_i to 0 + repeat while tf_i < tf_len: + set tf to get_list(try_funcs and tf_i) + if length_list(tf) > 5: + set rt to string_concat(get_list(tf and 5) and "") + if string_length(rt) > 0: + set en_i to 0 + set en_len to length_list(all_enums) + repeat while en_i < en_len: + set edef to get_list(all_enums and en_i) + if rt equals get_list(edef and 1): + set evs to get_list(edef and 2) + if length_list(evs) > 0: + set ok to append_list(try_keys and get_list(tf and 1)) + set ok to append_list(try_vals and get_list(get_list(evs and 0) and 0)) + set en_i to en_i + 1 + set tf_i to tf_i + 1 + # Emit top-level constant globals (file scope), their GC mark functions, and # an __ep_init_constants() that runs their initializers. Always emit the init # function (empty if no globals) so main can unconditionally call it. diff --git a/ep_parser.ep b/ep_parser.ep index d270ca7..0029222 100644 --- a/ep_parser.ep +++ b/ep_parser.ep @@ -838,22 +838,28 @@ define parse_function_async with state and is_async: set name to get_token_value(tok_name) set params to parse_param_list(state) + 0 - - # Skip optional 'returning Type' clause + + # Optional 'returning Type' clause — capture the type name (index 5 on the + # func node) so codegen can map a function to its declared return enum + # (used by `try`). + set ret_type to "" set next_ret to peek_token(state) if get_token_type(next_ret) == 43: # returning set dummy to advance_token(state) # consume "returning" - set dummy to advance_token(state) # consume the type name - + set ret_tok to advance_token(state) # the type name + set ret_type to get_token_value(ret_tok) + set ok to expect_token_type(state and 22) # : - + set tok_nl to peek_token(state) if get_token_type(tok_nl) == 28: # Newline set dummy to advance_token(state) - + set body to parse_block(state) + 0 - - return make_node_func(name and params and body and is_async) + + set fnode to make_node_func(name and params and body and is_async) + 0 + set ok to append_list(fnode and ret_type) # index 5: declared return type name + return fnode define parse_block with state: set ok to expect_token_type(state and 29) # INDENT From f0a4643d345366be780d431652f1c416088c68b0 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 13:12:04 +0100 Subject: [PATCH 32/51] docs: self-hosted parity now 48/54 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3be657d..8f84d32 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ the same generational GC, pointer-safe accessors, and OOM-guarded allocators. `epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh` (currently 40/54 runnable programs; 8/9 +tracked by `tests/run_epc_parity.sh` (currently 48/54 runnable programs; 8/9 compile-error tests correctly rejected by the `ep_check.ep` semantic pass). `epc check ` runs the checks without codegen. From 4d26578109510c1ef6a4ae865d894937084feb29 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 13:48:03 +0100 Subject: [PATCH 33/51] fix(self-host): don't free lists that escape via return (UAF) (parity 48->49/54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup freed EVERY local list at scope exit without escape analysis, so a function that builds a list and returns it inside a constructed value — e.g. json_parse_object's create_json_object(keys, values) — had keys/values free_list'd at L_cleanup, freeing data the returned node still referenced. The caller then saw empty/garbage collections (json_parse returned an empty object). Add var_returned_in_stmts: skip the cleanup free for any list that appears in a return expression (directly or as a call/constructor argument). The GC reclaims it. Matches the Rust compiler's move-out semantics; general fix for any build-and-return-a-collection function. Gates: FIXPOINT OK; parity 48->49/54 (test_json_limits); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 168 +++++++++++++++++++------------------- ep_codegen.ep | 51 +++++++++++- 2 files changed, 130 insertions(+), 89 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 7663da4..0258568 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -5026,6 +5026,7 @@ long long is_accessor_name(long long); long long is_borrow_expr(long long, long long, long long); long long scan_stmts_for_borrows(long long, long long, long long); long long collect_borrowed_vars(long long, long long, long long, long long); +long long var_returned_in_stmts(long long, long long); long long gen_function(long long, long long); long long gen_statement(long long, long long, long long, long long); long long gen_expr(long long, long long, long long, long long); @@ -5783,7 +5784,6 @@ long long create_token(long long type, long long value, long long line, long lon goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(tok); return ret_val; } @@ -6181,7 +6181,6 @@ long long lex_string_body(long long source, long long pos0, long long source_len ep_gc_pop_roots(6); free_list(str_chars); free_list(expr_chars); - free_list(res); return ret_val; } @@ -6960,7 +6959,6 @@ long long tokenize_source(long long source) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(3); - free_list(tokens); free_list(indent_stack); return ret_val; } @@ -7010,7 +7008,6 @@ long long make_node_int(long long val) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7034,7 +7031,6 @@ long long make_node_str(long long val) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7058,7 +7054,6 @@ long long make_node_ident(long long name) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7084,7 +7079,6 @@ long long make_node_binary(long long left, long long op, long long right) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7110,7 +7104,6 @@ long long make_node_comp(long long left, long long op, long long right) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7135,7 +7128,6 @@ long long make_node_call(long long name, long long args) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7160,7 +7152,6 @@ long long make_node_set(long long var, long long expr) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7184,7 +7175,6 @@ long long make_node_return(long long expr) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7208,7 +7198,6 @@ long long make_node_display(long long expr) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7234,7 +7223,6 @@ long long make_node_if(long long cond, long long then_b, long long else_b) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7259,7 +7247,6 @@ long long make_node_repeat_while(long long cond, long long body) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7286,7 +7273,6 @@ long long make_node_func(long long name, long long params, long long body, long goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7318,7 +7304,6 @@ long long make_node_program(long long imports, long long externals, long long fu goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7343,7 +7328,6 @@ long long make_node_spawn(long long func_name, long long args) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7368,7 +7352,6 @@ long long make_node_send(long long chan, long long val) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7391,7 +7374,6 @@ long long make_node_channel() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7415,7 +7397,6 @@ long long make_node_receive(long long chan) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7441,7 +7422,6 @@ long long make_node_external(long long name, long long params, long long ret_typ goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7465,7 +7445,6 @@ long long make_node_borrow(long long target) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7489,7 +7468,6 @@ long long make_node_await(long long target) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7515,7 +7493,6 @@ long long make_node_logical(long long left, long long op, long long right) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7540,7 +7517,6 @@ long long make_node_field_access(long long obj, long long field_name) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7566,7 +7542,6 @@ long long make_node_field_set(long long obj, long long field_name, long long val goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7591,7 +7566,6 @@ long long make_node_struct_create(long long struct_name, long long fields) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7617,7 +7591,6 @@ long long make_node_method_call(long long obj, long long method_name, long long goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7642,7 +7615,6 @@ long long make_node_enum_create(long long variant_name, long long args) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7667,7 +7639,6 @@ long long make_node_match(long long expr, long long arms) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7693,7 +7664,6 @@ long long make_node_for_each(long long var_name, long long iter_expr, long long goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7716,7 +7686,6 @@ long long make_node_break() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7739,7 +7708,6 @@ long long make_node_continue() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7763,7 +7731,6 @@ long long make_node_bool(long long val) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7787,7 +7754,6 @@ long long make_node_unary_not(long long expr) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7811,7 +7777,6 @@ long long make_node_try(long long expr) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7836,7 +7801,6 @@ long long make_node_closure(long long params, long long body) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7860,7 +7824,6 @@ long long make_node_list_lit(long long elements) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7884,7 +7847,6 @@ long long make_node_expr_stmt(long long expr) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7909,7 +7871,6 @@ long long make_node_struct_def(long long name, long long fields) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7934,7 +7895,6 @@ long long make_node_enum_def(long long name, long long variants) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7961,7 +7921,6 @@ long long make_node_method_def(long long method_name, long long struct_name, lon goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -7986,7 +7945,6 @@ long long make_node_trait_def(long long name, long long method_sigs) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -8012,7 +7970,6 @@ long long make_node_trait_impl(long long trait_name, long long type_name, long l goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(node); return ret_val; } @@ -8037,7 +7994,6 @@ long long create_parser_state(long long tokens) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(state); return ret_val; } @@ -11020,7 +10976,6 @@ long long create_codegen_state() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(state); return ret_val; } @@ -11958,6 +11913,86 @@ long long collect_borrowed_vars(long long stmts, long long params, long long bor return ret_val; } +long long var_returned_in_stmts(long long name, long long stmts) { + long long n = 0; + long long idx = 0; + long long stmt = 0; + long long t = 0; + long long ids = 0; + long long ok = 0; + long long eb = 0; + long long arms = 0; + long long an = 0; + long long ai = 0; + long long ret_val = 0; + + ep_gc_push_root(&ids); + ep_gc_maybe_collect(); + + n = length_list(stmts); + idx = 0LL; + while (idx < n) { + stmt = get_list(stmts, idx); + t = get_list(stmt, 0LL); + if (t == 8LL) { + { + long long tmp_val = create_list(); + free_list(ids); + ids = tmp_val; + } + ok = collect_idents_expr(get_list(stmt, 1LL), ids); + if (contains_string_val(ids, name) == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + } + if (t == 10LL) { + if (var_returned_in_stmts(name, get_list(stmt, 2LL)) == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + eb = get_list(stmt, 3LL); + if (eb != 0LL) { + if (var_returned_in_stmts(name, eb) == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + } + } + if (t == 11LL) { + if (var_returned_in_stmts(name, get_list(stmt, 2LL)) == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + } + if (t == 28LL) { + if (var_returned_in_stmts(name, get_list(stmt, 3LL)) == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + } + if (t == 27LL) { + arms = get_list(stmt, 2LL); + an = length_list(arms); + ai = 0LL; + while (ai < an) { + if (var_returned_in_stmts(name, get_list(get_list(arms, ai), 2LL)) == 1LL) { + ret_val = 1LL; + goto L_cleanup; + } + ai = (ai + 1LL); + } + } + idx = (idx + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + free_list(ids); + return ret_val; +} + long long gen_function(long long state, long long func) { long long name = 0; long long params = 0; @@ -12250,6 +12285,7 @@ long long gen_function(long long state, long long func) { if ((is_global == 0LL && is_borrowed == 0LL)) { t = map_get(var_types_keys, var_types_values, var_name); if (t == 4LL) { + if (var_returned_in_stmts(var_name, body) == 0LL) { cleanup_line = (long long)" free_list("; cleanup_line = string_concat(cleanup_line, var_name); cleanup_line = string_concat(cleanup_line, (long long)");\n"); @@ -12257,6 +12293,7 @@ long long gen_function(long long state, long long func) { } } } + } idx = (idx + 1LL); } ok = emit(state, (long long)" return ret_val;\n}\n\n"); @@ -13617,7 +13654,6 @@ long long get_c_main_source() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -13734,7 +13770,6 @@ long long get_c_test_main_source(long long program) { L_cleanup: ep_gc_pop_roots(3); free_list(test_cases); - free_list(lines); return ret_val; } @@ -13813,7 +13848,6 @@ long long collect_all_spawns(long long program) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(spawn_list); return ret_val; } @@ -13845,7 +13879,6 @@ long long clone_list(long long lst) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(new_lst); return ret_val; } @@ -15403,7 +15436,6 @@ long long ep_rt_core_0() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -15574,7 +15606,6 @@ long long ep_rt_core_1() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -15745,7 +15776,6 @@ long long ep_rt_core_2() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -15916,7 +15946,6 @@ long long ep_rt_core_3() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -16087,7 +16116,6 @@ long long ep_rt_core_4() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -16258,7 +16286,6 @@ long long ep_rt_core_5() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -16429,7 +16456,6 @@ long long ep_rt_core_6() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -16600,7 +16626,6 @@ long long ep_rt_core_7() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -16771,7 +16796,6 @@ long long ep_rt_core_8() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -16942,7 +16966,6 @@ long long ep_rt_core_9() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -17113,7 +17136,6 @@ long long ep_rt_core_10() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -17284,7 +17306,6 @@ long long ep_rt_core_11() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -17455,7 +17476,6 @@ long long ep_rt_core_12() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -17626,7 +17646,6 @@ long long ep_rt_core_13() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -17797,7 +17816,6 @@ long long ep_rt_core_14() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -17968,7 +17986,6 @@ long long ep_rt_core_15() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -18139,7 +18156,6 @@ long long ep_rt_core_16() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -18310,7 +18326,6 @@ long long ep_rt_core_17() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -18481,7 +18496,6 @@ long long ep_rt_core_18() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -18652,7 +18666,6 @@ long long ep_rt_core_19() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -18823,7 +18836,6 @@ long long ep_rt_core_20() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -18994,7 +19006,6 @@ long long ep_rt_core_21() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -19165,7 +19176,6 @@ long long ep_rt_core_22() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -19336,7 +19346,6 @@ long long ep_rt_core_23() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -19507,7 +19516,6 @@ long long ep_rt_core_24() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -19678,7 +19686,6 @@ long long ep_rt_core_25() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -19849,7 +19856,6 @@ long long ep_rt_core_26() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20020,7 +20026,6 @@ long long ep_rt_core_27() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20191,7 +20196,6 @@ long long ep_rt_core_28() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20362,7 +20366,6 @@ long long ep_rt_core_29() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20533,7 +20536,6 @@ long long ep_rt_core_30() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20621,7 +20623,6 @@ long long ep_rt_core_31() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20792,7 +20793,6 @@ long long ep_rt_builtins_0() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20824,7 +20824,6 @@ long long ep_rt_builtins_1() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(lines); return ret_val; } @@ -20879,7 +20878,6 @@ long long get_shared_runtime_source() { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(parts); return ret_val; } diff --git a/ep_codegen.ep b/ep_codegen.ep index ad59537..729ab90 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -898,6 +898,45 @@ define collect_borrowed_vars with stmts and params and borrowed_keys and borrowe return 0 # Generator Functions +# Does `name` appear in any `return` expression within these statements +# (directly or as a call/constructor argument)? Such a value escapes the function, so +# it must NOT be freed at cleanup (that was freeing lists returned inside a +# constructed object — a use-after-free). Recurses into nested control flow. +define var_returned_in_stmts with name and stmts: + set n to length_list(stmts) + set idx to 0 + repeat while idx < n: + set stmt to get_list(stmts and idx) + set t to get_list(stmt and 0) + if t == 8: # RETURN + set ids to create_list() + set ok to collect_idents_expr(get_list(stmt and 1) and ids) + if contains_string_val(ids and name) == 1: + return 1 + if t == 10: # IF + if var_returned_in_stmts(name and get_list(stmt and 2)) == 1: + return 1 + set eb to get_list(stmt and 3) + if eb != 0: + if var_returned_in_stmts(name and eb) == 1: + return 1 + if t == 11: # REPEAT_WHILE + if var_returned_in_stmts(name and get_list(stmt and 2)) == 1: + return 1 + if t == 28: # FOR_EACH + if var_returned_in_stmts(name and get_list(stmt and 3)) == 1: + return 1 + if t == 27: # MATCH + set arms to get_list(stmt and 2) + set an to length_list(arms) + set ai to 0 + repeat while ai < an: + if var_returned_in_stmts(name and get_list(get_list(arms and ai) and 2)) == 1: + return 1 + set ai to ai + 1 + set idx to idx + 1 + return 0 + define gen_function with state and func: set name to get_list(func and 1) set params to get_list(func and 2) @@ -1139,10 +1178,14 @@ define gen_function with state and func: if is_global == 0 && is_borrowed == 0: set t to map_get(var_types_keys and var_types_values and var_name) if t == 4: # TYPE_LIST - set cleanup_line to " free_list(" - set cleanup_line to string_concat(cleanup_line and var_name) - set cleanup_line to string_concat(cleanup_line and ");\n") - set ok to emit(state and cleanup_line) + # Skip lists that escape via a return (freeing them would + # free data referenced by the returned value). The GC + # reclaims them. + if var_returned_in_stmts(var_name and body) == 0: + set cleanup_line to " free_list(" + set cleanup_line to string_concat(cleanup_line and var_name) + set cleanup_line to string_concat(cleanup_line and ");\n") + set ok to emit(state and cleanup_line) set idx to idx + 1 set ok to emit(state and " return ret_val;\n}\n\n") From 31d7fa86b3fa8713997c27338f5445d39c165c0f Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 13:54:05 +0100 Subject: [PATCH 34/51] fix(self-host): 'set x to y' is aliasing, not a move (parity 49->51/54) The move check marked the RHS of 'set x to y' as moved, so a later use of y (e.g. 'set rel_path to safe_path' then 'string_length(safe_path)') was rejected as use-after-move. In a GC-managed language both names safely reference the same object, and the reference compiler permits it. Drop the move-mark for direct assignment; the move-while-borrowed check and cross-thread send/spawn ownership transfer are unchanged, so the rejection suite still enforces 8/9. Gates: FIXPOINT OK; parity 49->51/54 (test_static_path_traversal, test_sql_params); run_tests.sh 69/69; rejection 8/9; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 3 --- ep_codegen.ep | 10 +++++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 0258568..8fba35b 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -14176,9 +14176,6 @@ long long check_safety_stmts(long long func, long long stmts, long long var_keys ret_val = 0LL; goto L_cleanup; } - if ((src_t != 5LL && src_t != 6LL)) { - ok = map_put(state_keys, state_values, src, 2LL); - } } } ok = map_put(state_keys, state_values, name, 1LL); diff --git a/ep_codegen.ep b/ep_codegen.ep index 729ab90..3cfb626 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -2430,9 +2430,13 @@ define check_safety_stmts with func and stmts and var_keys and var_values and st display "Safety Error: Cannot move variable because it is currently borrowed:" set ok to display_string(src) return 0 - - if src_t != 5 && src_t != 6: - set ok to map_put(state_keys and state_values and src and 2) + + # `set x to y` is aliasing, not a move: both names + # safely reference the same GC-managed object (the + # reference compiler permits the later use of y). Do + # NOT mark src as moved. (Ownership transfer that + # matters — send/spawn across threads — is handled + # separately.) set ok to map_put(state_keys and state_values and name and 1) if type == 16: # NODE_SEND From cb56a79e202548e16a709fb8c9c7601e5a81cc13 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 14:01:04 +0100 Subject: [PATCH 35/51] docs: self-hosted parity 50/54 (stable) Corrects the number: the move-aliasing fix reliably unblocks test_static_path_traversal (49->50). test_sql_params extracts a query column as 0 (reads uninitialized memory in the self-hosted-compiled sqlite wrapper), so it is not a stable pass; counted as failing. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f84d32..919bec7 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ the same generational GC, pointer-safe accessors, and OOM-guarded allocators. `epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh` (currently 48/54 runnable programs; 8/9 +tracked by `tests/run_epc_parity.sh` (currently 50/54 runnable programs; 8/9 compile-error tests correctly rejected by the `ep_check.ep` semantic pass). `epc check ` runs the checks without codegen. From 0304b152256a5f0243c4f541440c105df96ee782 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 15:32:30 +0100 Subject: [PATCH 36/51] feat(self-host): iterator protocol for 'for each' over custom iterators (parity 50->51/54) 'for each x in iter' where iter implements the Iterator trait now uses the iterator protocol instead of the list protocol (which segfaulted treating the struct as a list). generate_c records struct types implementing Iterator (state slot 18); collect_var_types tags 'set v to create ' vars with a dedicated iterator type-code (9); for-each on a type-9 value emits a loop that calls next() until the enum's Done variant, binding the Next payload each pass. Gates: FIXPOINT OK; parity 50->51/54 (test_custom_iterator); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 57 ++++++++++++++++++++++++++++++++++++++- ep_codegen.ep | 44 ++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 8fba35b..7eabdeb 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -10971,6 +10971,7 @@ long long create_codegen_state() { ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, (create_list() + 0LL)); ret_val = state; state = 0; goto L_cleanup; @@ -11437,6 +11438,11 @@ long long collect_var_types(long long state, long long stmts, long long var_keys var_name = get_list(stmt, 1LL); expr = get_list(stmt, 2LL); t = infer_type(state, expr, var_keys, var_values); + if (get_list(expr, 0LL) == 24LL) { + if (contains_string_val(get_list(state, 18LL), get_list(expr, 1LL)) == 1LL) { + t = 9LL; + } + } ok = map_put(var_keys, var_values, var_name, t); } else { if (type == 10LL) { @@ -12367,6 +12373,11 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon long long iter_expr = 0; long long iter_str = 0; long long label = 0; + long long iter_t = 0; + long long il = 0; + long long bl = 0; + long long ib_len = 0; + long long ib_i = 0; long long ret_val = 0; ep_gc_push_root(&expr_str); @@ -12379,6 +12390,8 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon ep_gc_push_root(&obj_str); ep_gc_push_root(&iter_str); ep_gc_push_root(&label); + ep_gc_push_root(&il); + ep_gc_push_root(&bl); ep_gc_maybe_collect(); type = get_list(stmt, 0LL); @@ -12691,6 +12704,32 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon body = get_list(stmt, 3LL); iter_str = gen_expr(state, iter_expr, var_keys, var_values); label = get_new_label(state, (long long)"foreach"); + iter_t = infer_type(state, iter_expr, var_keys, var_values); + if (iter_t == 9LL) { + ok = emit(state, (long long)" {\n"); + il = (long long)" long long _it = "; + il = string_concat(il, iter_str); + il = string_concat(il, (long long)";\n"); + ok = emit(state, il); + ok = emit(state, (long long)" while (1) {\n"); + ok = emit(state, (long long)" long long _res = next(_it);\n"); + ok = emit(state, (long long)" if (_res == 0) break;\n"); + ok = emit(state, (long long)" if (((long long*)_res)[0] == EP_TAG_Done) break;\n"); + bl = (long long)" long long "; + bl = string_concat(bl, var_name); + bl = string_concat(bl, (long long)" = ((long long*)_res)[1];\n"); + ok = emit(state, bl); + ib_len = length_list(body); + ib_i = 0LL; + while (ib_i < ib_len) { + ok = gen_statement(state, get_list(body, ib_i), var_keys, var_values); + ib_i = (ib_i + 1LL); + } + ok = emit(state, (long long)" }\n"); + ok = emit(state, (long long)" }\n"); + ret_val = 0LL; + goto L_cleanup; + } ok = emit(state, (long long)" {\n"); line = (long long)" long long _iter = "; line = string_concat(line, iter_str); @@ -12739,7 +12778,7 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon ret_val = 0LL; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(10); + ep_gc_pop_roots(12); return ret_val; } @@ -14639,6 +14678,10 @@ long long analyze_safety(long long state, long long program) { long long generate_c(long long program, long long is_test_mode) { long long state = 0; long long ok = 0; + long long it_impls = 0; + long long it_i = 0; + long long it_len = 0; + long long it_impl = 0; long long safety_ok = 0; long long prog_len = 0; long long field_slots = 0; @@ -14804,6 +14847,18 @@ long long generate_c(long long program, long long is_test_mode) { state = tmp_val; } ok = analyze_return_types(state, program); + if (length_list(program) > 8LL) { + it_impls = get_list(program, 8LL); + it_i = 0LL; + it_len = length_list(it_impls); + while (it_i < it_len) { + it_impl = get_list(it_impls, it_i); + if ((strcmp((char*)string_concat(get_list(it_impl, 1LL), (long long)""), (char*)(long long)"Iterator") == 0)) { + ok = append_list(get_list(state, 18LL), get_list(it_impl, 2LL)); + } + it_i = (it_i + 1LL); + } + } safety_ok = analyze_safety(state, program); if (safety_ok == 0LL) { ret_val = (long long)""; diff --git a/ep_codegen.ep b/ep_codegen.ep index 3cfb626..bfdcffa 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -361,6 +361,7 @@ define create_codegen_state: set ok to append_list(state and (create_list() + 0)) # try_ok_vals (15): that enum's first (Ok) variant name set ok to append_list(state and (create_list() + 0)) # vpat_keys (16): variant name set ok to append_list(state and (create_list() + 0)) # vpat_vals (17): list of field type-codes for that variant + set ok to append_list(state and (create_list() + 0)) # iter_structs (18): struct names implementing the Iterator trait return state # Map an ErnosPlain type-annotation name to the codegen's coarse type code. @@ -645,6 +646,11 @@ define collect_var_types with state and stmts and var_keys and var_values: set var_name to get_list(stmt and 1) set expr to get_list(stmt and 2) set t to infer_type(state and expr and var_keys and var_values) + # A `create ` where S implements Iterator gives the var the + # iterator type (9), so `for each` dispatches to the iterator protocol. + if get_list(expr and 0) == 24: # NODE_STRUCT_CREATE + if contains_string_val(get_list(state and 18) and get_list(expr and 1)) == 1: + set t to 9 set ok to map_put(var_keys and var_values and var_name and t) else: if type == 10: # NODE_IF @@ -1499,6 +1505,32 @@ define gen_statement with state and stmt and var_keys and var_values: set body to get_list(stmt and 3) set iter_str to gen_expr(state and iter_expr and var_keys and var_values) set label to get_new_label(state and "foreach") + # Iterator protocol: if the iterable has the iterator type (9), loop by + # calling next() until it yields the enum's Done variant; otherwise it + # yields Next(value) whose payload is at slot [1]. + set iter_t to infer_type(state and iter_expr and var_keys and var_values) + if iter_t == 9: + set ok to emit(state and " {\n") + set il to " long long _it = " + set il to string_concat(il and iter_str) + set il to string_concat(il and ";\n") + set ok to emit(state and il) + set ok to emit(state and " while (1) {\n") + set ok to emit(state and " long long _res = next(_it);\n") + set ok to emit(state and " if (_res == 0) break;\n") + set ok to emit(state and " if (((long long*)_res)[0] == EP_TAG_Done) break;\n") + set bl to " long long " + set bl to string_concat(bl and var_name) + set bl to string_concat(bl and " = ((long long*)_res)[1];\n") + set ok to emit(state and bl) + set ib_len to length_list(body) + set ib_i to 0 + repeat while ib_i < ib_len: + set ok to gen_statement(state and get_list(body and ib_i) and var_keys and var_values) + set ib_i to ib_i + 1 + set ok to emit(state and " }\n") + set ok to emit(state and " }\n") + return 0 set ok to emit(state and " {\n") set line to " long long _iter = " set line to string_concat(line and iter_str) @@ -2734,6 +2766,18 @@ define analyze_safety with state and program: define generate_c with program and is_test_mode: set state to create_codegen_state() set ok to analyze_return_types(state and program) + # Record which struct types implement the Iterator trait, so `for each` over + # such a value uses the iterator protocol (call next() until Done) instead + # of the list protocol. + if length_list(program) > 8: + set it_impls to get_list(program and 8) + set it_i to 0 + set it_len to length_list(it_impls) + repeat while it_i < it_len: + set it_impl to get_list(it_impls and it_i) + if string_concat(get_list(it_impl and 1) and "") equals "Iterator": + set ok to append_list(get_list(state and 18) and get_list(it_impl and 2)) + set it_i to it_i + 1 set safety_ok to analyze_safety(state and program) if safety_ok == 0: return "" From 5439ab41fa3a132cc3b1781b80ae5325942d6f1e Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 15:50:34 +0100 Subject: [PATCH 37/51] fix(self-host): rely on GC for local-list cleanup (fixes transitive-escape UAF) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The earlier return-escape check only caught lists directly in a return expression. A list can also escape transitively — each 'row' appended into the returned 'rows' (sql_query_params), etc. Freeing those at cleanup corrupted the returned value. Since the generational GC reclaims unreachable lists after the roots are popped, drop the cleanup free_list entirely: correct, and simpler than a full transitive escape analysis. (sql_query_params now returns intact rows; display of the string column remains a separate Any-typing gap.) Gates: FIXPOINT OK; parity 51/54; run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 77 +-------------------------------------- ep_codegen.ep | 24 +++++------- 2 files changed, 10 insertions(+), 91 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 7eabdeb..d10ff1f 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -5482,9 +5482,6 @@ long long parse_all_modules(long long current_file, long long parsed_files, long goto L_cleanup; L_cleanup: ep_gc_pop_roots(8); - free_list(state); - free_list(mod_funcs); - free_list(mod_externals); return ret_val; } @@ -5747,18 +5744,6 @@ long long _main() { } L_cleanup: ep_gc_pop_roots(20); - free_list(all_functions); - free_list(all_externals); - free_list(all_struct_defs); - free_list(all_enum_defs); - free_list(all_method_defs); - free_list(all_trait_defs); - free_list(all_trait_impls); - free_list(all_constants); - free_list(parsed_files); - free_list(f_names); - free_list(empty_imports); - free_list(program_ast); return ret_val; } @@ -6179,8 +6164,6 @@ long long lex_string_body(long long source, long long pos0, long long source_len goto L_cleanup; L_cleanup: ep_gc_pop_roots(6); - free_list(str_chars); - free_list(expr_chars); return ret_val; } @@ -6959,7 +6942,6 @@ long long tokenize_source(long long source) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(3); - free_list(indent_stack); return ret_val; } @@ -10054,7 +10036,6 @@ long long check_program(long long program) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(errs); return ret_val; } @@ -10658,7 +10639,6 @@ long long string_concat(long long s1, long long s2) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(2); - free_list(lst); return ret_val; } @@ -10737,7 +10717,6 @@ long long cg_sanitize_name(long long name) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(2); - free_list(kws); return ret_val; } @@ -10835,8 +10814,6 @@ long long cg_int_to_str(long long n) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(3); - free_list(lst); - free_list(digits); return ret_val; } @@ -10895,7 +10872,6 @@ long long escape_string(long long s) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(2); - free_list(lst); return ret_val; } @@ -10936,7 +10912,6 @@ long long join_strings(long long lines) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(2); - free_list(lst); return ret_val; } @@ -11995,7 +11970,6 @@ long long var_returned_in_stmts(long long name, long long stmts) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(1); - free_list(ids); return ret_val; } @@ -12039,8 +12013,6 @@ long long gen_function(long long state, long long func) { long long stmt = 0; long long gc_count_str = 0; long long root_pop = 0; - long long is_borrowed = 0; - long long cleanup_line = 0; long long ret_val = 0; ep_gc_push_root(&struct_decl); @@ -12051,7 +12023,6 @@ long long gen_function(long long state, long long func) { ep_gc_push_root(&decl); ep_gc_push_root(&root_line); ep_gc_push_root(&root_pop); - ep_gc_push_root(&cleanup_line); ep_gc_maybe_collect(); name = get_list(func, 1LL); @@ -12285,28 +12256,13 @@ long long gen_function(long long state, long long func) { } p_i = (p_i + 1LL); } - if (is_param == 0LL) { - is_global = is_global_var(var_name); - is_borrowed = map_get(borrowed_keys, borrowed_values, var_name); - if ((is_global == 0LL && is_borrowed == 0LL)) { - t = map_get(var_types_keys, var_types_values, var_name); - if (t == 4LL) { - if (var_returned_in_stmts(var_name, body) == 0LL) { - cleanup_line = (long long)" free_list("; - cleanup_line = string_concat(cleanup_line, var_name); - cleanup_line = string_concat(cleanup_line, (long long)");\n"); - ok = emit(state, cleanup_line); - } - } - } - } idx = (idx + 1LL); } ok = emit(state, (long long)" return ret_val;\n}\n\n"); ret_val = 0LL; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(9); + ep_gc_pop_roots(8); return ret_val; } @@ -13645,14 +13601,6 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon goto L_cleanup; L_cleanup: ep_gc_pop_roots(38); - free_list(formatted_args); - free_list(args_str_list); - free_list(raw_names); - free_list(captured); - free_list(c_keys); - free_list(c_values); - free_list(b_keys); - free_list(b_values); return ret_val; } @@ -13808,7 +13756,6 @@ long long get_c_test_main_source(long long program) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(3); - free_list(test_cases); return ret_val; } @@ -14577,20 +14524,6 @@ long long check_safety_stmts(long long func, long long stmts, long long var_keys goto L_cleanup; L_cleanup: ep_gc_pop_roots(14); - free_list(then_state_keys); - free_list(then_state_values); - free_list(then_borrow_keys); - free_list(then_borrow_values); - free_list(then_count_keys); - free_list(then_count_values); - free_list(else_state_keys); - free_list(else_state_values); - free_list(else_borrow_keys); - free_list(else_borrow_values); - free_list(else_count_keys); - free_list(else_count_values); - free_list(start_state_keys); - free_list(start_state_values); return ret_val; } @@ -15310,14 +15243,6 @@ long long generate_c(long long program, long long is_test_mode) { goto L_cleanup; L_cleanup: ep_gc_pop_roots(22); - free_list(state); - free_list(field_slots); - free_list(tag_slots); - free_list(vp_codes); - free_list(gk); - free_list(gv); - free_list(spawn_list); - free_list(emitted_methods); return ret_val; } diff --git a/ep_codegen.ep b/ep_codegen.ep index bfdcffa..0d1249f 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -1178,22 +1178,16 @@ define gen_function with state and func: set is_param to 1 set p_i to p_i + 1 - if is_param == 0: - set is_global to is_global_var(var_name) - set is_borrowed to map_get(borrowed_keys and borrowed_values and var_name) - if is_global == 0 && is_borrowed == 0: - set t to map_get(var_types_keys and var_types_values and var_name) - if t == 4: # TYPE_LIST - # Skip lists that escape via a return (freeing them would - # free data referenced by the returned value). The GC - # reclaims them. - if var_returned_in_stmts(var_name and body) == 0: - set cleanup_line to " free_list(" - set cleanup_line to string_concat(cleanup_line and var_name) - set cleanup_line to string_concat(cleanup_line and ");\n") - set ok to emit(state and cleanup_line) set idx to idx + 1 - + + # NOTE: local lists are intentionally NOT free_list'd at cleanup. A list can + # escape not only via a direct `return` but transitively — appended into + # another list / stored into a struct that is returned (e.g. each `row` + # pushed into the returned `rows`). Freeing such a list corrupts the + # returned value (use-after-free). The generational GC reclaims unreachable + # lists after the roots are popped, so relying on it is both correct and + # simpler than a full transitive escape analysis. + set ok to emit(state and " return ret_val;\n}\n\n") return 0 From cd3c781d8b4516a64efc56dcf76fe53bfe4b3195 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 16:08:44 +0100 Subject: [PATCH 38/51] feat(self-host): auto-detect string vs int when displaying accessor results (parity ~51-52/54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit display of a direct get_list/pop_list/map_get_val/map_get_str call now routes through ep_auto_to_string, so a string element (e.g. a SQL column) prints as text rather than as its %lld pointer. Narrow/syntactic — ordinary int displays are unaffected (verified test_errors and the suite unchanged). Combined with the GC-based cleanup (no auto free_list), test_free_reassign (double-free) and test_json_limits (escape UAF) pass, and test_sql_params now prints correct rows (occasionally flaky via the ep_auto_to_string pointer probe on the non-GC-registered strdup'd column text). Gates: FIXPOINT OK; parity 51 stable (52 when sql_params's probe succeeds); run_tests.sh 69/69; no epc compile flakiness; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 29 ++++++++++++++++++++++++++++- ep_codegen.ep | 33 ++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index d10ff1f..ec500c5 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -12280,6 +12280,8 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon long long line = 0; long long expr_type = 0; long long null_line = 0; + long long is_any_call = 0; + long long callee = 0; long long cond = 0; long long then_b = 0; long long else_b = 0; @@ -12339,6 +12341,7 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon ep_gc_push_root(&expr_str); ep_gc_push_root(&line); ep_gc_push_root(&null_line); + ep_gc_push_root(&callee); ep_gc_push_root(&cond_str); ep_gc_push_root(&arg_str); ep_gc_push_root(&chan_str); @@ -12412,6 +12415,30 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon expr = get_list(stmt, 1LL); t = infer_type(state, expr, var_keys, var_values); expr_str = gen_expr(state, expr, var_keys, var_values); + is_any_call = 0LL; + if (get_list(expr, 0LL) == 6LL) { + callee = string_concat(get_list(expr, 1LL), (long long)""); + if ((strcmp((char*)callee, (char*)(long long)"get_list") == 0)) { + is_any_call = 1LL; + } + if ((strcmp((char*)callee, (char*)(long long)"pop_list") == 0)) { + is_any_call = 1LL; + } + if ((strcmp((char*)callee, (char*)(long long)"map_get_val") == 0)) { + is_any_call = 1LL; + } + if ((strcmp((char*)callee, (char*)(long long)"map_get_str") == 0)) { + is_any_call = 1LL; + } + } + if (is_any_call == 1LL) { + line = (long long)" printf(\"%s\\n\", (char*)ep_auto_to_string("; + line = string_concat(line, expr_str); + line = string_concat(line, (long long)"));\n"); + ok = emit(state, line); + ret_val = 0LL; + goto L_cleanup; + } if ((t == 2LL || t == 3LL)) { line = (long long)" printf(\"%s\\n\", (char*)"; line = string_concat(line, expr_str); @@ -12734,7 +12761,7 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon ret_val = 0LL; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(12); + ep_gc_pop_roots(13); return ret_val; } diff --git a/ep_codegen.ep b/ep_codegen.ep index 0d1249f..b6943ed 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -1180,13 +1180,12 @@ define gen_function with state and func: set idx to idx + 1 - # NOTE: local lists are intentionally NOT free_list'd at cleanup. A list can - # escape not only via a direct `return` but transitively — appended into - # another list / stored into a struct that is returned (e.g. each `row` - # pushed into the returned `rows`). Freeing such a list corrupts the - # returned value (use-after-free). The generational GC reclaims unreachable - # lists after the roots are popped, so relying on it is both correct and - # simpler than a full transitive escape analysis. + # Local lists are NOT free_list'd at cleanup. Auto-freeing here was unsound in + # two directions: it double-freed lists an explicit free_list already released + # (and reassigned), and it freed lists that escape — directly via `return` or + # transitively (a `row` appended into the returned `rows`) — corrupting the + # returned value. The generational GC reclaims unreachable lists after the + # roots are popped, which is both correct and simpler than escape analysis. set ok to emit(state and " return ret_val;\n}\n\n") return 0 @@ -1261,6 +1260,26 @@ define gen_statement with state and stmt and var_keys and var_values: set expr to get_list(stmt and 1) set t to infer_type(state and expr and var_keys and var_values) set expr_str to gen_expr(state and expr and var_keys and var_values) + # A direct heterogeneous-accessor call (get_list/pop_list/map_get*) + # yields an Any value that may be a string or an int; auto-detect at + # runtime. Narrow/syntactic so ordinary int displays are unaffected. + set is_any_call to 0 + if get_list(expr and 0) == 6: # NODE_CALL + set callee to string_concat(get_list(expr and 1) and "") + if callee equals "get_list": + set is_any_call to 1 + if callee equals "pop_list": + set is_any_call to 1 + if callee equals "map_get_val": + set is_any_call to 1 + if callee equals "map_get_str": + set is_any_call to 1 + if is_any_call == 1: + set line to " printf(\"%s\\n\", (char*)ep_auto_to_string(" + set line to string_concat(line and expr_str) + set line to string_concat(line and "));\n") + set ok to emit(state and line) + return 0 if t == 2 || t == 3: # TYPE_STR or TYPE_DYNSTR set line to " printf(\"%s\\n\", (char*)" set line to string_concat(line and expr_str) From 0465c044ada93fe0edb8df3c31ec3b73dab765e7 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 16:19:44 +0100 Subject: [PATCH 39/51] fix(runtime): GC-register sqlite column text; parity stable at 52/54 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ep_sqlite3_column_text strdup'd the column but left it untracked, so it leaked and ep_auto_to_string had to guess via a memory probe (flaky). Register the copy with the GC (EP_OBJ_STRING): reclaimed properly, and detected deterministically as a string. Shared runtime — ep_runtime_gen.ep regenerated; Rust emission picks it up via include_str!. Gates: FIXPOINT OK; epc parity 52/54 STABLE across 5 runs (was flaky 51-52); run_tests.sh 69/69; bootstrap regenerated. Remaining: test_async_loop (valid output, nondeterministic worker order) and test_task_group (async scheduler). Co-Authored-By: Claude Fable 5 --- README.md | 2 +- bootstrap/epc_bootstrap.c | 88 +++++++++++++++++++++------------------ ep_runtime_gen.ep | 80 ++++++++++++++++++----------------- runtime/ep_runtime.c | 8 +++- 4 files changed, 97 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 919bec7..b47ef1e 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ the same generational GC, pointer-safe accessors, and OOM-guarded allocators. `epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh` (currently 50/54 runnable programs; 8/9 +tracked by `tests/run_epc_parity.sh` (currently 52/54 runnable programs; 8/9 compile-error tests correctly rejected by the `ep_check.ep` semantic pass). `epc check ` runs the checks without codegen. diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index ec500c5..9f69276 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -3353,8 +3353,12 @@ long long ep_sqlite3_column_count(long long stmt) { long long ep_sqlite3_column_text(long long stmt, long long col) { const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col); - if (!t) return (long long)strdup(""); - return (long long)strdup((const char*)t); + char* copy = (!t) ? strdup("") : strdup((const char*)t); + /* Register the copy with the GC so it is reclaimed (not leaked) and so + ep_auto_to_string recognizes it as a string deterministically via + ep_gc_find, rather than relying on the memory-probe heuristic. */ + if (copy) ep_gc_register(copy, EP_OBJ_STRING); + return (long long)copy; } long long ep_sqlite3_column_int(long long stmt, long long col) { @@ -19081,8 +19085,12 @@ long long ep_rt_core_22() { ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long ep_sqlite3_column_text(long long stmt, long long col) {\n"); ok = append_list(lines, (long long)" const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);\n"); - ok = append_list(lines, (long long)" if (!t) return (long long)strdup(\"\");\n"); - ok = append_list(lines, (long long)" return (long long)strdup((const char*)t);\n"); + ok = append_list(lines, (long long)" char* copy = (!t) ? strdup(\"\") : strdup((const char*)t);\n"); + ok = append_list(lines, (long long)" /* Register the copy with the GC so it is reclaimed (not leaked) and so\n"); + ok = append_list(lines, (long long)" ep_auto_to_string recognizes it as a string deterministically via\n"); + ok = append_list(lines, (long long)" ep_gc_find, rather than relying on the memory-probe heuristic. */\n"); + ok = append_list(lines, (long long)" if (copy) ep_gc_register(copy, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)copy;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long ep_sqlite3_column_int(long long stmt, long long col) {\n"); @@ -19172,10 +19180,6 @@ long long ep_rt_core_22() { ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"char* string_from_list(long long list_ptr) {\n"); ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); - ok = append_list(lines, (long long)" if (!list) {\n"); - ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); - ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); - ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19196,6 +19200,10 @@ long long ep_rt_core_23() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" if (!list) {\n"); + ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); ok = append_list(lines, (long long)" return empty;\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" char* s = malloc(list->length + 1);\n"); @@ -19342,10 +19350,6 @@ long long ep_rt_core_23() { ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 0;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_is_directory(long long path_ptr) {\n"); - ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); - ok = append_list(lines, (long long)" struct stat st;\n"); - ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19366,6 +19370,10 @@ long long ep_rt_core_24() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"long long ep_is_directory(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n"); ok = append_list(lines, (long long)" return S_ISDIR(st.st_mode) ? 1 : 0;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); @@ -19512,10 +19520,6 @@ long long ep_rt_core_24() { ok = append_list(lines, (long long)" return (long long)\"\";\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_os_name(void) {\n"); - ok = append_list(lines, (long long)" #if defined(__APPLE__)\n"); - ok = append_list(lines, (long long)" return (long long)\"macos\";\n"); - ok = append_list(lines, (long long)" #elif defined(__linux__)\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19536,6 +19540,10 @@ long long ep_rt_core_25() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"long long ep_os_name(void) {\n"); + ok = append_list(lines, (long long)" #if defined(__APPLE__)\n"); + ok = append_list(lines, (long long)" return (long long)\"macos\";\n"); + ok = append_list(lines, (long long)" #elif defined(__linux__)\n"); ok = append_list(lines, (long long)" return (long long)\"linux\";\n"); ok = append_list(lines, (long long)" #elif defined(_WIN32)\n"); ok = append_list(lines, (long long)" return (long long)\"windows\";\n"); @@ -19682,10 +19690,6 @@ long long ep_rt_core_25() { ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"long long ep_rwlock_unlock(long long rwl) {\n"); ok = append_list(lines, (long long)" /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n"); - ok = append_list(lines, (long long)" In practice the caller should know which lock was taken.\n"); - ok = append_list(lines, (long long)" ReleaseSRWLockExclusive on a shared lock is undefined, but\n"); - ok = append_list(lines, (long long)" the runtime guarantees matched lock/unlock pairs. We default\n"); - ok = append_list(lines, (long long)" to releasing the exclusive lock; shared unlock is handled\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19706,6 +19710,10 @@ long long ep_rt_core_26() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" In practice the caller should know which lock was taken.\n"); + ok = append_list(lines, (long long)" ReleaseSRWLockExclusive on a shared lock is undefined, but\n"); + ok = append_list(lines, (long long)" the runtime guarantees matched lock/unlock pairs. We default\n"); + ok = append_list(lines, (long long)" to releasing the exclusive lock; shared unlock is handled\n"); ok = append_list(lines, (long long)" by pairing read_lock -> read_unlock if needed later. */\n"); ok = append_list(lines, (long long)" ReleaseSRWLockExclusive((SRWLOCK*)rwl);\n"); ok = append_list(lines, (long long)" return 1;\n"); @@ -19852,10 +19860,6 @@ long long ep_rt_core_26() { ok = append_list(lines, (long long)" pthread_mutex_init(&s->mutex, NULL);\n"); ok = append_list(lines, (long long)" pthread_cond_init(&s->cond, NULL);\n"); ok = append_list(lines, (long long)" s->value = initial;\n"); - ok = append_list(lines, (long long)" return (long long)s;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_semaphore_wait(long long sp) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19876,6 +19880,10 @@ long long ep_rt_core_27() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" return (long long)s;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_semaphore_wait(long long sp) {\n"); ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n"); ok = append_list(lines, (long long)" pthread_mutex_lock(&s->mutex);\n"); ok = append_list(lines, (long long)" while (s->value <= 0) {\n"); @@ -20022,10 +20030,6 @@ long long ep_rt_core_27() { ok = append_list(lines, (long long)" regmatch_t match;\n"); ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); - ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n"); - ok = append_list(lines, (long long)" if (ret) {\n"); - ok = append_list(lines, (long long)" append_list(list, text_ptr);\n"); - ok = append_list(lines, (long long)" return list;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20046,6 +20050,10 @@ long long ep_rt_core_28() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n"); + ok = append_list(lines, (long long)" if (ret) {\n"); + ok = append_list(lines, (long long)" append_list(list, text_ptr);\n"); + ok = append_list(lines, (long long)" return list;\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" const char* cursor = text;\n"); ok = append_list(lines, (long long)" while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n"); @@ -20192,10 +20200,6 @@ long long ep_rt_core_28() { ok = append_list(lines, (long long)" *dst = '\\0';\n"); ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); ok = append_list(lines, (long long)" return (long long)result;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* ========== Additional String Functions ========== */\n"); - ok = append_list(lines, (long long)"#include \n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20216,6 +20220,10 @@ long long ep_rt_core_29() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* ========== Additional String Functions ========== */\n"); + ok = append_list(lines, (long long)"#include \n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long string_upper(long long s_val) {\n"); ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n"); @@ -20362,10 +20370,6 @@ long long ep_rt_core_29() { ok = append_list(lines, (long long)" return min + (long long)(r % range);\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"// JSON built-in functions\n"); - ok = append_list(lines, (long long)"static const char* json_skip_ws(const char* p) {\n"); - ok = append_list(lines, (long long)" while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n"); - ok = append_list(lines, (long long)" return p;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20386,6 +20390,10 @@ long long ep_rt_core_30() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"// JSON built-in functions\n"); + ok = append_list(lines, (long long)"static const char* json_skip_ws(const char* p) {\n"); + ok = append_list(lines, (long long)" while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n"); + ok = append_list(lines, (long long)" return p;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"static const char* json_skip_value(const char* p) {\n"); @@ -20532,10 +20540,6 @@ long long ep_rt_core_30() { ok = append_list(lines, (long long)" unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n"); ok = append_list(lines, (long long)" e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n"); ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" free(msg);\n"); - ok = append_list(lines, (long long)"\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20556,6 +20560,10 @@ long long ep_rt_core_31() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" free(msg);\n"); + ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)" // Return Base64-encoded hash directly (for WebSocket handshake)\n"); ok = append_list(lines, (long long)" unsigned char hash[20];\n"); ok = append_list(lines, (long long)" hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;\n"); diff --git a/ep_runtime_gen.ep b/ep_runtime_gen.ep index 339b091..22a3a12 100644 --- a/ep_runtime_gen.ep +++ b/ep_runtime_gen.ep @@ -3447,8 +3447,12 @@ define ep_rt_core_22: set ok to append_list(lines and "\n") set ok to append_list(lines and "long long ep_sqlite3_column_text(long long stmt, long long col) {\n") set ok to append_list(lines and " const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);\n") - set ok to append_list(lines and " if (!t) return (long long)strdup(\"\");\n") - set ok to append_list(lines and " return (long long)strdup((const char*)t);\n") + set ok to append_list(lines and " char* copy = (!t) ? strdup(\"\") : strdup((const char*)t);\n") + set ok to append_list(lines and " /* Register the copy with the GC so it is reclaimed (not leaked) and so\n") + set ok to append_list(lines and " ep_auto_to_string recognizes it as a string deterministically via\n") + set ok to append_list(lines and " ep_gc_find, rather than relying on the memory-probe heuristic. */\n") + set ok to append_list(lines and " if (copy) ep_gc_register(copy, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)copy;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "long long ep_sqlite3_column_int(long long stmt, long long col) {\n") @@ -3538,14 +3542,14 @@ define ep_rt_core_22: set ok to append_list(lines and "\n") set ok to append_list(lines and "char* string_from_list(long long list_ptr) {\n") set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - set ok to append_list(lines and " if (!list) {\n") - set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") - set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") return join_strings(lines) define ep_rt_core_23: set lines to create_list() + set ok to append_list(lines and " if (!list) {\n") + set ok to append_list(lines and " char* empty = malloc(1);\n") + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") set ok to append_list(lines and " return empty;\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " char* s = malloc(list->length + 1);\n") @@ -3692,14 +3696,14 @@ define ep_rt_core_23: set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 0;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_is_directory(long long path_ptr) {\n") - set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") - set ok to append_list(lines and " struct stat st;\n") - set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") return join_strings(lines) define ep_rt_core_24: set lines to create_list() + set ok to append_list(lines and "long long ep_is_directory(long long path_ptr) {\n") + set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") + set ok to append_list(lines and " struct stat st;\n") + set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n") set ok to append_list(lines and " return S_ISDIR(st.st_mode) ? 1 : 0;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") @@ -3846,14 +3850,14 @@ define ep_rt_core_24: set ok to append_list(lines and " return (long long)\"\";\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_os_name(void) {\n") - set ok to append_list(lines and " #if defined(__APPLE__)\n") - set ok to append_list(lines and " return (long long)\"macos\";\n") - set ok to append_list(lines and " #elif defined(__linux__)\n") return join_strings(lines) define ep_rt_core_25: set lines to create_list() + set ok to append_list(lines and "long long ep_os_name(void) {\n") + set ok to append_list(lines and " #if defined(__APPLE__)\n") + set ok to append_list(lines and " return (long long)\"macos\";\n") + set ok to append_list(lines and " #elif defined(__linux__)\n") set ok to append_list(lines and " return (long long)\"linux\";\n") set ok to append_list(lines and " #elif defined(_WIN32)\n") set ok to append_list(lines and " return (long long)\"windows\";\n") @@ -4000,14 +4004,14 @@ define ep_rt_core_25: set ok to append_list(lines and "}\n") set ok to append_list(lines and "long long ep_rwlock_unlock(long long rwl) {\n") set ok to append_list(lines and " /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n") - set ok to append_list(lines and " In practice the caller should know which lock was taken.\n") - set ok to append_list(lines and " ReleaseSRWLockExclusive on a shared lock is undefined, but\n") - set ok to append_list(lines and " the runtime guarantees matched lock/unlock pairs. We default\n") - set ok to append_list(lines and " to releasing the exclusive lock; shared unlock is handled\n") return join_strings(lines) define ep_rt_core_26: set lines to create_list() + set ok to append_list(lines and " In practice the caller should know which lock was taken.\n") + set ok to append_list(lines and " ReleaseSRWLockExclusive on a shared lock is undefined, but\n") + set ok to append_list(lines and " the runtime guarantees matched lock/unlock pairs. We default\n") + set ok to append_list(lines and " to releasing the exclusive lock; shared unlock is handled\n") set ok to append_list(lines and " by pairing read_lock -> read_unlock if needed later. */\n") set ok to append_list(lines and " ReleaseSRWLockExclusive((SRWLOCK*)rwl);\n") set ok to append_list(lines and " return 1;\n") @@ -4154,14 +4158,14 @@ define ep_rt_core_26: set ok to append_list(lines and " pthread_mutex_init(&s->mutex, NULL);\n") set ok to append_list(lines and " pthread_cond_init(&s->cond, NULL);\n") set ok to append_list(lines and " s->value = initial;\n") - set ok to append_list(lines and " return (long long)s;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_semaphore_wait(long long sp) {\n") return join_strings(lines) define ep_rt_core_27: set lines to create_list() + set ok to append_list(lines and " return (long long)s;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_semaphore_wait(long long sp) {\n") set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n") set ok to append_list(lines and " pthread_mutex_lock(&s->mutex);\n") set ok to append_list(lines and " while (s->value <= 0) {\n") @@ -4308,14 +4312,14 @@ define ep_rt_core_27: set ok to append_list(lines and " regmatch_t match;\n") set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n") set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n") - set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n") - set ok to append_list(lines and " if (ret) {\n") - set ok to append_list(lines and " append_list(list, text_ptr);\n") - set ok to append_list(lines and " return list;\n") return join_strings(lines) define ep_rt_core_28: set lines to create_list() + set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n") + set ok to append_list(lines and " if (ret) {\n") + set ok to append_list(lines and " append_list(list, text_ptr);\n") + set ok to append_list(lines and " return list;\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " const char* cursor = text;\n") set ok to append_list(lines and " while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n") @@ -4462,14 +4466,14 @@ define ep_rt_core_28: set ok to append_list(lines and " *dst = '\\0';\n") set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") set ok to append_list(lines and " return (long long)result;\n") - set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* ========== Additional String Functions ========== */\n") - set ok to append_list(lines and "#include \n") return join_strings(lines) define ep_rt_core_29: set lines to create_list() + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "/* ========== Additional String Functions ========== */\n") + set ok to append_list(lines and "#include \n") set ok to append_list(lines and "\n") set ok to append_list(lines and "long long string_upper(long long s_val) {\n") set ok to append_list(lines and " const char* s = (const char*)s_val;\n") @@ -4616,14 +4620,14 @@ define ep_rt_core_29: set ok to append_list(lines and " return min + (long long)(r % range);\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - set ok to append_list(lines and "// JSON built-in functions\n") - set ok to append_list(lines and "static const char* json_skip_ws(const char* p) {\n") - set ok to append_list(lines and " while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n") - set ok to append_list(lines and " return p;\n") return join_strings(lines) define ep_rt_core_30: set lines to create_list() + set ok to append_list(lines and "// JSON built-in functions\n") + set ok to append_list(lines and "static const char* json_skip_ws(const char* p) {\n") + set ok to append_list(lines and " while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n") + set ok to append_list(lines and " return p;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "static const char* json_skip_value(const char* p) {\n") @@ -4770,14 +4774,14 @@ define ep_rt_core_30: set ok to append_list(lines and " unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n") set ok to append_list(lines and " e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n") set ok to append_list(lines and " }\n") - set ok to append_list(lines and " h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n") - set ok to append_list(lines and " }\n") - set ok to append_list(lines and " free(msg);\n") - set ok to append_list(lines and "\n") return join_strings(lines) define ep_rt_core_31: set lines to create_list() + set ok to append_list(lines and " h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " free(msg);\n") + set ok to append_list(lines and "\n") set ok to append_list(lines and " // Return Base64-encoded hash directly (for WebSocket handshake)\n") set ok to append_list(lines and " unsigned char hash[20];\n") set ok to append_list(lines and " hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;\n") diff --git a/runtime/ep_runtime.c b/runtime/ep_runtime.c index 6316c5e..1480e60 100644 --- a/runtime/ep_runtime.c +++ b/runtime/ep_runtime.c @@ -3353,8 +3353,12 @@ long long ep_sqlite3_column_count(long long stmt) { long long ep_sqlite3_column_text(long long stmt, long long col) { const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col); - if (!t) return (long long)strdup(""); - return (long long)strdup((const char*)t); + char* copy = (!t) ? strdup("") : strdup((const char*)t); + /* Register the copy with the GC so it is reclaimed (not leaked) and so + ep_auto_to_string recognizes it as a string deterministically via + ep_gc_find, rather than relying on the memory-probe heuristic. */ + if (copy) ep_gc_register(copy, EP_OBJ_STRING); + return (long long)copy; } long long ep_sqlite3_column_int(long long stmt, long long col) { From 53134f1a9c1f2c0e01af06f4210b257ca932d7fc Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 19:29:23 +0100 Subject: [PATCH 40/51] fix(runtime): wait_task_group polls thread-completed futures (task_group works) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-hosted compiler emits THREAD-based async (a detached pthread runs the body and sets fut->completed), but wait_task_group only drove the coroutine run-queue — so with an empty run-queue it declared deadlock while worker threads were still finishing. Poll (bounded, 1ms) for future completion instead of exiting. The Rust compiler's coroutine async keeps the run-queue/timers busy so it never reaches this branch; behavior there is unchanged. Gates: FIXPOINT OK; test_task_group now runs to completion (exit 0); run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 630 ++++++++++++++++++++------------------ ep_runtime_gen.ep | 235 +++++++------- runtime/ep_runtime.c | 17 +- 3 files changed, 463 insertions(+), 419 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 9f69276..cb81ab8 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -562,7 +562,8 @@ static long long add_task_group(long long group_ptr, long long fut_ptr) { static long long wait_task_group(long long group_ptr) { EpTaskGroup* tg = (EpTaskGroup*)group_ptr; if (!tg) return 0; - + + long long ep_wait_group_spin = 0; int all_done = 0; while (!all_done) { all_done = 1; @@ -608,9 +609,19 @@ static long long wait_task_group(long long group_ptr) { } else { long long timeout = ep_get_next_timer_timeout(); if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { - fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n"); - exit(1); + /* No coroutine tasks/timers/IO to drive. The futures may still be + completed by detached worker THREADS (the self-hosted compiler + emits thread-based async), so poll for their completion rather + than declaring deadlock. Bounded so a genuinely stuck group + still fails instead of hanging forever. */ + ep_sleep_ms(1); + if (++ep_wait_group_spin > 60000) { + fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n"); + exit(1); + } + continue; } + ep_wait_group_spin = 0; if (ep_event_loop_fd == -1) { if (timeout > 0) { ep_sleep_ms(timeout); @@ -15914,7 +15925,8 @@ long long ep_rt_core_3() { ok = append_list(lines, (long long)"static long long wait_task_group(long long group_ptr) {\n"); ok = append_list(lines, (long long)" EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n"); ok = append_list(lines, (long long)" if (!tg) return 0;\n"); - ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" long long ep_wait_group_spin = 0;\n"); ok = append_list(lines, (long long)" int all_done = 0;\n"); ok = append_list(lines, (long long)" while (!all_done) {\n"); ok = append_list(lines, (long long)" all_done = 1;\n"); @@ -15949,7 +15961,6 @@ long long ep_rt_core_3() { ok = append_list(lines, (long long)" task->fut->completed = 1;\n"); ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n"); ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n"); - ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -15970,6 +15981,7 @@ long long ep_rt_core_4() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" free(task->args);\n"); @@ -15980,9 +15992,19 @@ long long ep_rt_core_4() { ok = append_list(lines, (long long)" } else {\n"); ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n"); ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n"); - ok = append_list(lines, (long long)" fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n"); - ok = append_list(lines, (long long)" exit(1);\n"); + ok = append_list(lines, (long long)" /* No coroutine tasks/timers/IO to drive. The futures may still be\n"); + ok = append_list(lines, (long long)" completed by detached worker THREADS (the self-hosted compiler\n"); + ok = append_list(lines, (long long)" emits thread-based async), so poll for their completion rather\n"); + ok = append_list(lines, (long long)" than declaring deadlock. Bounded so a genuinely stuck group\n"); + ok = append_list(lines, (long long)" still fails instead of hanging forever. */\n"); + ok = append_list(lines, (long long)" ep_sleep_ms(1);\n"); + ok = append_list(lines, (long long)" if (++ep_wait_group_spin > 60000) {\n"); + ok = append_list(lines, (long long)" fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n"); + ok = append_list(lines, (long long)" exit(1);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" continue;\n"); ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_wait_group_spin = 0;\n"); ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n"); ok = append_list(lines, (long long)" if (timeout > 0) {\n"); ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n"); @@ -16109,17 +16131,6 @@ long long ep_rt_core_4() { ok = append_list(lines, (long long)" `await async_wait_readable(fd)` suspends the calling async task until `fd` is\n"); ok = append_list(lines, (long long)" readable, letting the event loop run other tasks (e.g. another agent waiting on\n"); ok = append_list(lines, (long long)" its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a\n"); - ok = append_list(lines, (long long)" oneshot read-readiness task with the loop, return the future. When fd becomes\n"); - ok = append_list(lines, (long long)" readable, ep_async_wait_step re-enqueues the task; its step completes the future\n"); - ok = append_list(lines, (long long)" and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n"); - ok = append_list(lines, (long long)" on ONE thread — no OS threads, no shared-heap GC race. */\n"); - ok = append_list(lines, (long long)"typedef struct { EpFuture* fut; } EpReadReadyArgs;\n"); - ok = append_list(lines, (long long)"static long long ep_read_ready_step(void* r) {\n"); - ok = append_list(lines, (long long)" EpReadReadyArgs* args = (EpReadReadyArgs*)r;\n"); - ok = append_list(lines, (long long)" if (args && args->fut) {\n"); - ok = append_list(lines, (long long)" args->fut->completed = 1;\n"); - ok = append_list(lines, (long long)" args->fut->value = 1;\n"); - ok = append_list(lines, (long long)" if (args->fut->waiting_task) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -16140,6 +16151,17 @@ long long ep_rt_core_5() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" oneshot read-readiness task with the loop, return the future. When fd becomes\n"); + ok = append_list(lines, (long long)" readable, ep_async_wait_step re-enqueues the task; its step completes the future\n"); + ok = append_list(lines, (long long)" and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n"); + ok = append_list(lines, (long long)" on ONE thread — no OS threads, no shared-heap GC race. */\n"); + ok = append_list(lines, (long long)"typedef struct { EpFuture* fut; } EpReadReadyArgs;\n"); + ok = append_list(lines, (long long)"static long long ep_read_ready_step(void* r) {\n"); + ok = append_list(lines, (long long)" EpReadReadyArgs* args = (EpReadReadyArgs*)r;\n"); + ok = append_list(lines, (long long)" if (args && args->fut) {\n"); + ok = append_list(lines, (long long)" args->fut->completed = 1;\n"); + ok = append_list(lines, (long long)" args->fut->value = 1;\n"); + ok = append_list(lines, (long long)" if (args->fut->waiting_task) {\n"); ok = append_list(lines, (long long)" ep_task_enqueue(args->fut->waiting_task);\n"); ok = append_list(lines, (long long)" args->fut->waiting_task = NULL;\n"); ok = append_list(lines, (long long)" }\n"); @@ -16279,17 +16301,6 @@ long long ep_rt_core_5() { ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* Thread registry for GC root scanning in multi-threaded environment */\n"); ok = append_list(lines, (long long)"#define EP_MAX_THREADS 256\n"); - ok = append_list(lines, (long long)"static __thread void* volatile ep_thread_local_top = NULL;\n"); - ok = append_list(lines, (long long)"static __thread void* ep_thread_local_bottom = NULL;\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n"); - ok = append_list(lines, (long long)"static void* ep_thread_bottoms[EP_MAX_THREADS];\n"); - ok = append_list(lines, (long long)"static volatile int ep_thread_active[EP_MAX_THREADS];\n"); - ok = append_list(lines, (long long)"static int ep_num_threads = 0;\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* Per-thread GC root state — heap-allocated, stable across thread lifetime.\n"); - ok = append_list(lines, (long long)" Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,\n"); - ok = append_list(lines, (long long)" ep_gc_root_sp) in the global registry. When a thread exited, the __thread\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -16310,6 +16321,17 @@ long long ep_rt_core_6() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"static __thread void* volatile ep_thread_local_top = NULL;\n"); + ok = append_list(lines, (long long)"static __thread void* ep_thread_local_bottom = NULL;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n"); + ok = append_list(lines, (long long)"static void* ep_thread_bottoms[EP_MAX_THREADS];\n"); + ok = append_list(lines, (long long)"static volatile int ep_thread_active[EP_MAX_THREADS];\n"); + ok = append_list(lines, (long long)"static int ep_num_threads = 0;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Per-thread GC root state — heap-allocated, stable across thread lifetime.\n"); + ok = append_list(lines, (long long)" Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,\n"); + ok = append_list(lines, (long long)" ep_gc_root_sp) in the global registry. When a thread exited, the __thread\n"); ok = append_list(lines, (long long)" storage was freed, leaving dangling pointers that ep_gc_mark would\n"); ok = append_list(lines, (long long)" dereference → segfault. Now each thread gets a heap-allocated state struct\n"); ok = append_list(lines, (long long)" that survives thread exit and is only recycled when the slot is reused. */\n"); @@ -16449,17 +16471,6 @@ long long ep_rt_core_6() { ok = append_list(lines, (long long)" for (int i = 0; i < ep_num_threads; i++) {\n"); ok = append_list(lines, (long long)" if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n"); ok = append_list(lines, (long long)" /* Zero root count FIRST — even if ep_gc_mark races past the\n"); - ok = append_list(lines, (long long)" active check, it will see sp=0 and walk no roots instead\n"); - ok = append_list(lines, (long long)" of dereferencing stale __thread pointers */\n"); - ok = append_list(lines, (long long)" if (ep_thread_gc_states[i]) {\n"); - ok = append_list(lines, (long long)" ep_thread_gc_states[i]->sp = 0;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */\n"); - ok = append_list(lines, (long long)" ep_thread_active[i] = 0;\n"); - ok = append_list(lines, (long long)" ep_thread_slot = -1;\n"); - ok = append_list(lines, (long long)" break;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" }\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -16480,6 +16491,17 @@ long long ep_rt_core_7() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" active check, it will see sp=0 and walk no roots instead\n"); + ok = append_list(lines, (long long)" of dereferencing stale __thread pointers */\n"); + ok = append_list(lines, (long long)" if (ep_thread_gc_states[i]) {\n"); + ok = append_list(lines, (long long)" ep_thread_gc_states[i]->sp = 0;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */\n"); + ok = append_list(lines, (long long)" ep_thread_active[i] = 0;\n"); + ok = append_list(lines, (long long)" ep_thread_slot = -1;\n"); + ok = append_list(lines, (long long)" break;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); @@ -16619,17 +16641,6 @@ long long ep_rt_core_7() { ok = append_list(lines, (long long)"static EpGCObject* ep_gc_find(void* ptr) {\n"); ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint */\n"); - ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n"); - ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); - ok = append_list(lines, (long long)" return obj;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).\n"); - ok = append_list(lines, (long long)" The whole operation runs under ep_gc_mutex so the table lookups and the\n"); - ok = append_list(lines, (long long)" remembered-set update see a consistent table (no race with a concurrent\n"); - ok = append_list(lines, (long long)" resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */\n"); - ok = append_list(lines, (long long)"static void ep_gc_write_barrier(void* host_ptr, long long val) {\n"); - ok = append_list(lines, (long long)" if (val == 0) return;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -16650,6 +16661,17 @@ long long ep_rt_core_8() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" return obj;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).\n"); + ok = append_list(lines, (long long)" The whole operation runs under ep_gc_mutex so the table lookups and the\n"); + ok = append_list(lines, (long long)" remembered-set update see a consistent table (no race with a concurrent\n"); + ok = append_list(lines, (long long)" resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_write_barrier(void* host_ptr, long long val) {\n"); + ok = append_list(lines, (long long)" if (val == 0) return;\n"); ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */\n"); ok = append_list(lines, (long long)" EpGCObject* host_obj = ep_gc_table_get(host_ptr);\n"); @@ -16789,17 +16811,6 @@ long long ep_rt_core_8() { ok = append_list(lines, (long long)" order the compiler laid them out (a missed _regs would drop a register-only root). */\n"); ok = append_list(lines, (long long)" { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;\n"); ok = append_list(lines, (long long)" ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }\n"); - ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n"); - ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n"); - ok = append_list(lines, (long long)" if (!ep_thread_tops[t]) continue;\n"); - ok = append_list(lines, (long long)" /* The published top comes from a char local, so it may not be pointer-aligned;\n"); - ok = append_list(lines, (long long)" mask DOWN to 8 bytes. Aligning down only widens the conservative window by a\n"); - ok = append_list(lines, (long long)" few harmless bytes — aligning up could skip the slot holding a live root.\n"); - ok = append_list(lines, (long long)" Unaligned void** dereferences are UB and produce a skewed scan window on\n"); - ok = append_list(lines, (long long)" strict platforms (caught by valgrind on Linux). */\n"); - ok = append_list(lines, (long long)" void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);\n"); - ok = append_list(lines, (long long)" void** end = (void**)ep_thread_bottoms[t];\n"); - ok = append_list(lines, (long long)" if (!start || !end) continue;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -16820,6 +16831,17 @@ long long ep_rt_core_9() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n"); + ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n"); + ok = append_list(lines, (long long)" if (!ep_thread_tops[t]) continue;\n"); + ok = append_list(lines, (long long)" /* The published top comes from a char local, so it may not be pointer-aligned;\n"); + ok = append_list(lines, (long long)" mask DOWN to 8 bytes. Aligning down only widens the conservative window by a\n"); + ok = append_list(lines, (long long)" few harmless bytes — aligning up could skip the slot holding a live root.\n"); + ok = append_list(lines, (long long)" Unaligned void** dereferences are UB and produce a skewed scan window on\n"); + ok = append_list(lines, (long long)" strict platforms (caught by valgrind on Linux). */\n"); + ok = append_list(lines, (long long)" void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);\n"); + ok = append_list(lines, (long long)" void** end = (void**)ep_thread_bottoms[t];\n"); + ok = append_list(lines, (long long)" if (!start || !end) continue;\n"); ok = append_list(lines, (long long)" if (start > end) { void** tmp = start; start = end; end = tmp; }\n"); ok = append_list(lines, (long long)" for (void** cur = start; cur < end; cur++) {\n"); ok = append_list(lines, (long long)" void* p = *cur;\n"); @@ -16959,17 +16981,6 @@ long long ep_rt_core_9() { ok = append_list(lines, (long long)" if (val != 0) {\n"); ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)val);\n"); ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" /* Mark active tasks in the scheduler run queue for minor collection */\n"); - ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n"); - ok = append_list(lines, (long long)" while (task) {\n"); - ok = append_list(lines, (long long)" if (task->fut) {\n"); - ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)task->fut);\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" if (task->args && task->args_size_bytes > 0) {\n"); - ok = append_list(lines, (long long)" long long* ptr = (long long*)task->args;\n"); - ok = append_list(lines, (long long)" for (int i = 0; i < task->args_size_bytes / 8; i++) {\n"); - ok = append_list(lines, (long long)" long long val = ptr[i];\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -16990,6 +17001,17 @@ long long ep_rt_core_10() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Mark active tasks in the scheduler run queue for minor collection */\n"); + ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n"); + ok = append_list(lines, (long long)" while (task) {\n"); + ok = append_list(lines, (long long)" if (task->fut) {\n"); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)task->fut);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (task->args && task->args_size_bytes > 0) {\n"); + ok = append_list(lines, (long long)" long long* ptr = (long long*)task->args;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < task->args_size_bytes / 8; i++) {\n"); + ok = append_list(lines, (long long)" long long val = ptr[i];\n"); ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object_minor((void*)val);\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" }\n"); @@ -17129,17 +17151,6 @@ long long ep_rt_core_10() { ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function\n"); ok = append_list(lines, (long long)" GC safepoint: if another thread has stopped the world, park here until it's done. */\n"); - ok = append_list(lines, (long long)"static void ep_gc_maybe_collect(void) {\n"); - ok = append_list(lines, (long long)" if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n"); - ok = append_list(lines, (long long)" /* Safepoint: lock-free fast check, then park under the lock if a collection\n"); - ok = append_list(lines, (long long)" is in progress on another thread. Keeps the no-GC path lock-free. */\n"); - ok = append_list(lines, (long long)" if (ep_gc_stop_requested) {\n"); - ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); - ok = append_list(lines, (long long)" ep_gc_park_if_stopped();\n"); - ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" /* Fast path: check thresholds before acquiring mutex.\n"); - ok = append_list(lines, (long long)" Counters are only incremented under the mutex, so worst case\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17160,6 +17171,17 @@ long long ep_rt_core_11() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"static void ep_gc_maybe_collect(void) {\n"); + ok = append_list(lines, (long long)" if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n"); + ok = append_list(lines, (long long)" /* Safepoint: lock-free fast check, then park under the lock if a collection\n"); + ok = append_list(lines, (long long)" is in progress on another thread. Keeps the no-GC path lock-free. */\n"); + ok = append_list(lines, (long long)" if (ep_gc_stop_requested) {\n"); + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" ep_gc_park_if_stopped();\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" /* Fast path: check thresholds before acquiring mutex.\n"); + ok = append_list(lines, (long long)" Counters are only incremented under the mutex, so worst case\n"); ok = append_list(lines, (long long)" we miss one collection cycle — safe trade-off for avoiding\n"); ok = append_list(lines, (long long)" a mutex lock/unlock (~20-50ns) on every function call. */\n"); ok = append_list(lines, (long long)" if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;\n"); @@ -17299,17 +17321,6 @@ long long ep_rt_core_11() { ok = append_list(lines, (long long)"static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"static void ep_register_channel(EpChannel* chan) {\n"); - ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); - ok = append_list(lines, (long long)" if (ep_channel_count < EP_MAX_CHANNELS) {\n"); - ok = append_list(lines, (long long)" ep_channel_registry[ep_channel_count++] = chan;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* Channel scanning implementations — called by GC mark via function pointers.\n"); - ok = append_list(lines, (long long)" These are defined here (after EpChannel) so they can access struct fields. */\n"); - ok = append_list(lines, (long long)"static void ep_gc_mark_object(void* ptr); /* forward decl */\n"); - ok = append_list(lines, (long long)"static void ep_gc_mark_object_minor(void* ptr); /* forward decl */\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17330,6 +17341,17 @@ long long ep_rt_core_12() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)" if (ep_channel_count < EP_MAX_CHANNELS) {\n"); + ok = append_list(lines, (long long)" ep_channel_registry[ep_channel_count++] = chan;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Channel scanning implementations — called by GC mark via function pointers.\n"); + ok = append_list(lines, (long long)" These are defined here (after EpChannel) so they can access struct fields. */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_object(void* ptr); /* forward decl */\n"); + ok = append_list(lines, (long long)"static void ep_gc_mark_object_minor(void* ptr); /* forward decl */\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"static void ep_gc_scan_channels_major_impl(void) {\n"); ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); @@ -17469,17 +17491,6 @@ long long ep_rt_core_12() { ok = append_list(lines, (long long)" // Poll all channels\n"); ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n"); ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)list->data[i];\n"); - ok = append_list(lines, (long long)" if (chan) {\n"); - ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); - ok = append_list(lines, (long long)" if (chan->size > 0) {\n"); - ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); - ok = append_list(lines, (long long)" return i;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" \n"); - ok = append_list(lines, (long long)" // Check timeout\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17500,6 +17511,17 @@ long long ep_rt_core_13() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" if (chan) {\n"); + ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" if (chan->size > 0) {\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" return i;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" \n"); + ok = append_list(lines, (long long)" // Check timeout\n"); ok = append_list(lines, (long long)" if (timeout_ms >= 0) {\n"); ok = append_list(lines, (long long)"#ifdef _WIN32\n"); ok = append_list(lines, (long long)" ULONGLONG now_tick = GetTickCount64();\n"); @@ -17639,17 +17661,6 @@ long long ep_rt_core_13() { ok = append_list(lines, (long long)" setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n"); ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n"); ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n"); - ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n"); - ok = append_list(lines, (long long)" serv_addr.sin_addr.s_addr = INADDR_ANY;\n"); - ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n"); - ok = append_list(lines, (long long)" if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n"); - ok = append_list(lines, (long long)"#ifdef _WIN32\n"); - ok = append_list(lines, (long long)" closesocket(sockfd);\n"); - ok = append_list(lines, (long long)"#else\n"); - ok = append_list(lines, (long long)" close(sockfd);\n"); - ok = append_list(lines, (long long)"#endif\n"); - ok = append_list(lines, (long long)" return -1;\n"); - ok = append_list(lines, (long long)" }\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17670,6 +17681,17 @@ long long ep_rt_core_14() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n"); + ok = append_list(lines, (long long)" serv_addr.sin_addr.s_addr = INADDR_ANY;\n"); + ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n"); + ok = append_list(lines, (long long)" if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n"); + ok = append_list(lines, (long long)"#ifdef _WIN32\n"); + ok = append_list(lines, (long long)" closesocket(sockfd);\n"); + ok = append_list(lines, (long long)"#else\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)"#endif\n"); + ok = append_list(lines, (long long)" return -1;\n"); + ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" if (listen(sockfd, 10) < 0) {\n"); ok = append_list(lines, (long long)"#ifdef _WIN32\n"); ok = append_list(lines, (long long)" closesocket(sockfd);\n"); @@ -17809,17 +17831,6 @@ long long ep_rt_core_14() { ok = append_list(lines, (long long)"long long ep_dlcall0(long long fptr) {\n"); ok = append_list(lines, (long long)" return ((ep_fn0)fptr)();\n"); ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"long long ep_dlcall1(long long fptr, long long a0) {\n"); - ok = append_list(lines, (long long)" return ((ep_fn1)fptr)(a0);\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"long long ep_dlcall2(long long fptr, long long a0, long long a1) {\n"); - ok = append_list(lines, (long long)" return ((ep_fn2)fptr)(a0, a1);\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {\n"); - ok = append_list(lines, (long long)" return ((ep_fn3)fptr)(a0, a1, a2);\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n"); - ok = append_list(lines, (long long)" return ((ep_fn4)fptr)(a0, a1, a2, a3);\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17840,6 +17851,17 @@ long long ep_rt_core_15() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"long long ep_dlcall1(long long fptr, long long a0) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn1)fptr)(a0);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall2(long long fptr, long long a0, long long a1) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn2)fptr)(a0, a1);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn3)fptr)(a0, a1, a2);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n"); + ok = append_list(lines, (long long)" return ((ep_fn4)fptr)(a0, a1, a2, a3);\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n"); ok = append_list(lines, (long long)" return ((ep_fn5)fptr)(a0, a1, a2, a3, a4);\n"); @@ -17979,17 +18001,6 @@ long long ep_rt_core_15() { ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\n"); ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)map->entries[i].key);\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long create_map(void) {\n"); - ok = append_list(lines, (long long)" EpMap* map = malloc(sizeof(EpMap));\n"); - ok = append_list(lines, (long long)" if (!map) return 0;\n"); - ok = append_list(lines, (long long)" map->capacity = 16;\n"); - ok = append_list(lines, (long long)" map->size = 0;\n"); - ok = append_list(lines, (long long)" map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n"); - ok = append_list(lines, (long long)" if (!map->entries) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18010,6 +18021,17 @@ long long ep_rt_core_16() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long create_map(void) {\n"); + ok = append_list(lines, (long long)" EpMap* map = malloc(sizeof(EpMap));\n"); + ok = append_list(lines, (long long)" if (!map) return 0;\n"); + ok = append_list(lines, (long long)" map->capacity = 16;\n"); + ok = append_list(lines, (long long)" map->size = 0;\n"); + ok = append_list(lines, (long long)" map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n"); + ok = append_list(lines, (long long)" if (!map->entries) {\n"); ok = append_list(lines, (long long)" free(map);\n"); ok = append_list(lines, (long long)" return 0;\n"); ok = append_list(lines, (long long)" }\n"); @@ -18149,17 +18171,6 @@ long long ep_rt_core_16() { ok = append_list(lines, (long long)" char* k = map->entries[next_h].key;\n"); ok = append_list(lines, (long long)" long long v = map->entries[next_h].value;\n"); ok = append_list(lines, (long long)" map->entries[next_h].key = NULL;\n"); - ok = append_list(lines, (long long)" map->entries[next_h].value = 0;\n"); - ok = append_list(lines, (long long)" map->entries[next_h].used = 0;\n"); - ok = append_list(lines, (long long)" map->size--;\n"); - ok = append_list(lines, (long long)" map_insert(map_ptr, (long long)k, v);\n"); - ok = append_list(lines, (long long)" free(k);\n"); - ok = append_list(lines, (long long)" next_h = (next_h + 1) % map->capacity;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" return 1;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n"); - ok = append_list(lines, (long long)" if (h == start_h) break;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18180,6 +18191,17 @@ long long ep_rt_core_17() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" map->entries[next_h].value = 0;\n"); + ok = append_list(lines, (long long)" map->entries[next_h].used = 0;\n"); + ok = append_list(lines, (long long)" map->size--;\n"); + ok = append_list(lines, (long long)" map_insert(map_ptr, (long long)k, v);\n"); + ok = append_list(lines, (long long)" free(k);\n"); + ok = append_list(lines, (long long)" next_h = (next_h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n"); + ok = append_list(lines, (long long)" if (h == start_h) break;\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" return 0;\n"); ok = append_list(lines, (long long)"}\n"); @@ -18319,17 +18341,6 @@ long long ep_rt_core_17() { ok = append_list(lines, (long long)" free(dq->data);\n"); ok = append_list(lines, (long long)" free(dq);\n"); ok = append_list(lines, (long long)" return 0;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* Filesystem Operations */\n"); - ok = append_list(lines, (long long)"#include \n"); - ok = append_list(lines, (long long)"#include \n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long fs_scan_dir(long long path_val) {\n"); - ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); - ok = append_list(lines, (long long)" long long list_ptr = create_list();\n"); - ok = append_list(lines, (long long)" if (!path) return list_ptr;\n"); - ok = append_list(lines, (long long)" DIR* d = opendir(path);\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18350,6 +18361,17 @@ long long ep_rt_core_18() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* Filesystem Operations */\n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"#include \n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long fs_scan_dir(long long path_val) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n"); + ok = append_list(lines, (long long)" long long list_ptr = create_list();\n"); + ok = append_list(lines, (long long)" if (!path) return list_ptr;\n"); + ok = append_list(lines, (long long)" DIR* d = opendir(path);\n"); ok = append_list(lines, (long long)" if (!d) return list_ptr;\n"); ok = append_list(lines, (long long)" struct dirent* dir;\n"); ok = append_list(lines, (long long)" while ((dir = readdir(d)) != NULL) {\n"); @@ -18489,17 +18511,6 @@ long long ep_rt_core_18() { ok = append_list(lines, (long long)" \"Host: %s\\r\\n\"\n"); ok = append_list(lines, (long long)" \"Content-Length: %zu\\r\\n\"\n"); ok = append_list(lines, (long long)" \"Connection: close\\r\\n\"\n"); - ok = append_list(lines, (long long)" \"%s%s\"\n"); - ok = append_list(lines, (long long)" \"\\r\\n\",\n"); - ok = append_list(lines, (long long)" method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n"); - ok = append_list(lines, (long long)" if (send(sockfd, req, req_len, 0) < 0) {\n"); - ok = append_list(lines, (long long)" close(sockfd);\n"); - ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send failed\");\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" if (body_len > 0) {\n"); - ok = append_list(lines, (long long)" if (send(sockfd, body, body_len, 0) < 0) {\n"); - ok = append_list(lines, (long long)" close(sockfd);\n"); - ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send body failed\");\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18520,6 +18531,17 @@ long long ep_rt_core_19() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" \"%s%s\"\n"); + ok = append_list(lines, (long long)" \"\\r\\n\",\n"); + ok = append_list(lines, (long long)" method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n"); + ok = append_list(lines, (long long)" if (send(sockfd, req, req_len, 0) < 0) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send failed\");\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" if (body_len > 0) {\n"); + ok = append_list(lines, (long long)" if (send(sockfd, body, body_len, 0) < 0) {\n"); + ok = append_list(lines, (long long)" close(sockfd);\n"); + ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send body failed\");\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" size_t resp_cap = 4096;\n"); @@ -18659,17 +18681,6 @@ long long ep_rt_core_19() { ok = append_list(lines, (long long)" char* result = malloc(65);\n"); ok = append_list(lines, (long long)" if (result) {\n"); ok = append_list(lines, (long long)" for (int i = 0; i < 32; i++) {\n"); - ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" result[64] = '\\0';\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" return result;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary\n"); - ok = append_list(lines, (long long)" safe), so keys/messages containing NUL bytes hash correctly. Returns a\n"); - ok = append_list(lines, (long long)" malloc'd 64-char lowercase hex string. */\n"); - ok = append_list(lines, (long long)"long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18690,6 +18701,17 @@ long long ep_rt_core_20() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" result[64] = '\\0';\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" return result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary\n"); + ok = append_list(lines, (long long)" safe), so keys/messages containing NUL bytes hash correctly. Returns a\n"); + ok = append_list(lines, (long long)" malloc'd 64-char lowercase hex string. */\n"); + ok = append_list(lines, (long long)"long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {\n"); ok = append_list(lines, (long long)" const unsigned char* key = (const unsigned char*)key_ptr;\n"); ok = append_list(lines, (long long)" const unsigned char* msg = (const unsigned char*)msg_ptr;\n"); ok = append_list(lines, (long long)" size_t klen = (size_t)key_len;\n"); @@ -18829,17 +18851,6 @@ long long ep_rt_core_20() { ok = append_list(lines, (long long)" unsigned char bits[8];\n"); ok = append_list(lines, (long long)" bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n"); ok = append_list(lines, (long long)" bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n"); - ok = append_list(lines, (long long)" unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n"); - ok = append_list(lines, (long long)" unsigned char padding[64];\n"); - ok = append_list(lines, (long long)" memset(padding, 0, 64); padding[0] = 0x80;\n"); - ok = append_list(lines, (long long)" ep_md5_update(ctx, padding, pad_len);\n"); - ok = append_list(lines, (long long)" ep_md5_update(ctx, bits, 8);\n"); - ok = append_list(lines, (long long)" for (int i = 0; i < 4; i++) {\n"); - ok = append_list(lines, (long long)" digest[i*4] = ctx->state[i];\n"); - ok = append_list(lines, (long long)" digest[i*4 + 1] = ctx->state[i] >> 8;\n"); - ok = append_list(lines, (long long)" digest[i*4 + 2] = ctx->state[i] >> 16;\n"); - ok = append_list(lines, (long long)" digest[i*4 + 3] = ctx->state[i] >> 24;\n"); - ok = append_list(lines, (long long)" }\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18860,6 +18871,17 @@ long long ep_rt_core_21() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n"); + ok = append_list(lines, (long long)" unsigned char padding[64];\n"); + ok = append_list(lines, (long long)" memset(padding, 0, 64); padding[0] = 0x80;\n"); + ok = append_list(lines, (long long)" ep_md5_update(ctx, padding, pad_len);\n"); + ok = append_list(lines, (long long)" ep_md5_update(ctx, bits, 8);\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 4; i++) {\n"); + ok = append_list(lines, (long long)" digest[i*4] = ctx->state[i];\n"); + ok = append_list(lines, (long long)" digest[i*4 + 1] = ctx->state[i] >> 8;\n"); + ok = append_list(lines, (long long)" digest[i*4 + 2] = ctx->state[i] >> 16;\n"); + ok = append_list(lines, (long long)" digest[i*4 + 3] = ctx->state[i] >> 24;\n"); + ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"char* ep_md5(const char* s) {\n"); @@ -18999,17 +19021,6 @@ long long ep_rt_core_21() { ok = append_list(lines, (long long)"long long sqlite_get_callback_ptr(long long dummy) {\n"); ok = append_list(lines, (long long)" return (long long)sqlite_list_callback;\n"); ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* SQLite type-safe wrappers — marshal between int and long long */\n"); - ok = append_list(lines, (long long)"#ifdef EP_HAS_SQLITE\n"); - ok = append_list(lines, (long long)"typedef struct sqlite3 sqlite3;\n"); - ok = append_list(lines, (long long)"int sqlite3_open(const char*, sqlite3**);\n"); - ok = append_list(lines, (long long)"int sqlite3_close(sqlite3*);\n"); - ok = append_list(lines, (long long)"int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_sqlite3_open(long long filename, long long db_ptr) {\n"); - ok = append_list(lines, (long long)" sqlite3* db = NULL;\n"); - ok = append_list(lines, (long long)" int rc = sqlite3_open((const char*)filename, &db);\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19030,6 +19041,17 @@ long long ep_rt_core_22() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"/* SQLite type-safe wrappers — marshal between int and long long */\n"); + ok = append_list(lines, (long long)"#ifdef EP_HAS_SQLITE\n"); + ok = append_list(lines, (long long)"typedef struct sqlite3 sqlite3;\n"); + ok = append_list(lines, (long long)"int sqlite3_open(const char*, sqlite3**);\n"); + ok = append_list(lines, (long long)"int sqlite3_close(sqlite3*);\n"); + ok = append_list(lines, (long long)"int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_sqlite3_open(long long filename, long long db_ptr) {\n"); + ok = append_list(lines, (long long)" sqlite3* db = NULL;\n"); + ok = append_list(lines, (long long)" int rc = sqlite3_open((const char*)filename, &db);\n"); ok = append_list(lines, (long long)" if (rc == 0 && db_ptr != 0) {\n"); ok = append_list(lines, (long long)" *((long long*)db_ptr) = (long long)db;\n"); ok = append_list(lines, (long long)" }\n"); @@ -19169,17 +19191,6 @@ long long ep_rt_core_22() { ok = append_list(lines, (long long)" if (!sub) {\n"); ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); - ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); - ok = append_list(lines, (long long)" return empty;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" strncpy(sub, s + start, len);\n"); - ok = append_list(lines, (long long)" sub[len] = '\\0';\n"); - ok = append_list(lines, (long long)" ep_gc_register(sub, EP_OBJ_STRING);\n"); - ok = append_list(lines, (long long)" return sub;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"char* string_from_list(long long list_ptr) {\n"); - ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19200,6 +19211,17 @@ long long ep_rt_core_23() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return empty;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" strncpy(sub, s + start, len);\n"); + ok = append_list(lines, (long long)" sub[len] = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(sub, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return sub;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"char* string_from_list(long long list_ptr) {\n"); + ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n"); ok = append_list(lines, (long long)" if (!list) {\n"); ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); @@ -19339,17 +19361,6 @@ long long ep_rt_core_23() { ok = append_list(lines, (long long)" const char* content = (const char*)content_ptr;\n"); ok = append_list(lines, (long long)" FILE* f = fopen(path, \"ab\");\n"); ok = append_list(lines, (long long)" if (!f) return 0;\n"); - ok = append_list(lines, (long long)" fputs(content, f);\n"); - ok = append_list(lines, (long long)" fclose(f);\n"); - ok = append_list(lines, (long long)" return 1;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_file_exists(long long path_ptr) {\n"); - ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); - ok = append_list(lines, (long long)" struct stat st;\n"); - ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 0;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19370,6 +19381,17 @@ long long ep_rt_core_24() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" fputs(content, f);\n"); + ok = append_list(lines, (long long)" fclose(f);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_file_exists(long long path_ptr) {\n"); + ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); + ok = append_list(lines, (long long)" struct stat st;\n"); + ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long ep_is_directory(long long path_ptr) {\n"); ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); ok = append_list(lines, (long long)" struct stat st;\n"); @@ -19509,17 +19531,6 @@ long long ep_rt_core_24() { ok = append_list(lines, (long long)" return val ? (long long)val : (long long)\"\";\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_setenv(long long name_ptr, long long val_ptr) {\n"); - ok = append_list(lines, (long long)" return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_get_cwd(void) {\n"); - ok = append_list(lines, (long long)" char* buf = (char*)malloc(4096);\n"); - ok = append_list(lines, (long long)" if (getcwd(buf, 4096)) return (long long)buf;\n"); - ok = append_list(lines, (long long)" free(buf);\n"); - ok = append_list(lines, (long long)" return (long long)\"\";\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19540,6 +19551,17 @@ long long ep_rt_core_25() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"long long ep_setenv(long long name_ptr, long long val_ptr) {\n"); + ok = append_list(lines, (long long)" return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_get_cwd(void) {\n"); + ok = append_list(lines, (long long)" char* buf = (char*)malloc(4096);\n"); + ok = append_list(lines, (long long)" if (getcwd(buf, 4096)) return (long long)buf;\n"); + ok = append_list(lines, (long long)" free(buf);\n"); + ok = append_list(lines, (long long)" return (long long)\"\";\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long ep_os_name(void) {\n"); ok = append_list(lines, (long long)" #if defined(__APPLE__)\n"); ok = append_list(lines, (long long)" return (long long)\"macos\";\n"); @@ -19679,17 +19701,6 @@ long long ep_rt_core_25() { ok = append_list(lines, (long long)" SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n"); ok = append_list(lines, (long long)" InitializeSRWLock(rwl);\n"); ok = append_list(lines, (long long)" return (long long)rwl;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n"); - ok = append_list(lines, (long long)" AcquireSRWLockShared((SRWLOCK*)rwl);\n"); - ok = append_list(lines, (long long)" return 1;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"long long ep_rwlock_write_lock(long long rwl) {\n"); - ok = append_list(lines, (long long)" AcquireSRWLockExclusive((SRWLOCK*)rwl);\n"); - ok = append_list(lines, (long long)" return 1;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"long long ep_rwlock_unlock(long long rwl) {\n"); - ok = append_list(lines, (long long)" /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19710,6 +19721,17 @@ long long ep_rt_core_26() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n"); + ok = append_list(lines, (long long)" AcquireSRWLockShared((SRWLOCK*)rwl);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_write_lock(long long rwl) {\n"); + ok = append_list(lines, (long long)" AcquireSRWLockExclusive((SRWLOCK*)rwl);\n"); + ok = append_list(lines, (long long)" return 1;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"long long ep_rwlock_unlock(long long rwl) {\n"); + ok = append_list(lines, (long long)" /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n"); ok = append_list(lines, (long long)" In practice the caller should know which lock was taken.\n"); ok = append_list(lines, (long long)" ReleaseSRWLockExclusive on a shared lock is undefined, but\n"); ok = append_list(lines, (long long)" the runtime guarantees matched lock/unlock pairs. We default\n"); @@ -19849,17 +19871,6 @@ long long ep_rt_core_26() { ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* Semaphore via mutex+condvar (portable) */\n"); - ok = append_list(lines, (long long)"typedef struct {\n"); - ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n"); - ok = append_list(lines, (long long)" pthread_cond_t cond;\n"); - ok = append_list(lines, (long long)" long long value;\n"); - ok = append_list(lines, (long long)"} EpSemaphore;\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_semaphore_create(long long initial) {\n"); - ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));\n"); - ok = append_list(lines, (long long)" pthread_mutex_init(&s->mutex, NULL);\n"); - ok = append_list(lines, (long long)" pthread_cond_init(&s->cond, NULL);\n"); - ok = append_list(lines, (long long)" s->value = initial;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19880,6 +19891,17 @@ long long ep_rt_core_27() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)"typedef struct {\n"); + ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n"); + ok = append_list(lines, (long long)" pthread_cond_t cond;\n"); + ok = append_list(lines, (long long)" long long value;\n"); + ok = append_list(lines, (long long)"} EpSemaphore;\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_semaphore_create(long long initial) {\n"); + ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));\n"); + ok = append_list(lines, (long long)" pthread_mutex_init(&s->mutex, NULL);\n"); + ok = append_list(lines, (long long)" pthread_cond_init(&s->cond, NULL);\n"); + ok = append_list(lines, (long long)" s->value = initial;\n"); ok = append_list(lines, (long long)" return (long long)s;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); @@ -20019,17 +20041,6 @@ long long ep_rt_core_27() { ok = append_list(lines, (long long)" memcpy(result + match.rm_so, repl, rlen);\n"); ok = append_list(lines, (long long)" memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\n"); ok = append_list(lines, (long long)" result[new_len] = '\\0';\n"); - ok = append_list(lines, (long long)" regfree(®ex);\n"); - ok = append_list(lines, (long long)" return (long long)result;\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_regex_split(long long pattern_ptr, long long text_ptr) {\n"); - ok = append_list(lines, (long long)" long long list = create_list();\n"); - ok = append_list(lines, (long long)" /* Simple split: find matches and split around them */\n"); - ok = append_list(lines, (long long)" regex_t regex;\n"); - ok = append_list(lines, (long long)" regmatch_t match;\n"); - ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); - ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20050,6 +20061,17 @@ long long ep_rt_core_28() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" regfree(®ex);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_regex_split(long long pattern_ptr, long long text_ptr) {\n"); + ok = append_list(lines, (long long)" long long list = create_list();\n"); + ok = append_list(lines, (long long)" /* Simple split: find matches and split around them */\n"); + ok = append_list(lines, (long long)" regex_t regex;\n"); + ok = append_list(lines, (long long)" regmatch_t match;\n"); + ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n"); + ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n"); ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n"); ok = append_list(lines, (long long)" if (ret) {\n"); ok = append_list(lines, (long long)" append_list(list, text_ptr);\n"); @@ -20189,17 +20211,6 @@ long long ep_rt_core_28() { ok = append_list(lines, (long long)" char* dst = result;\n"); ok = append_list(lines, (long long)" p = s;\n"); ok = append_list(lines, (long long)" while (*p) {\n"); - ok = append_list(lines, (long long)" if (strncmp(p, old_str, old_len) == 0) {\n"); - ok = append_list(lines, (long long)" memcpy(dst, new_str, new_len);\n"); - ok = append_list(lines, (long long)" dst += new_len;\n"); - ok = append_list(lines, (long long)" p += old_len;\n"); - ok = append_list(lines, (long long)" } else {\n"); - ok = append_list(lines, (long long)" *dst++ = *p++;\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" *dst = '\\0';\n"); - ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); - ok = append_list(lines, (long long)" return (long long)result;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20220,6 +20231,17 @@ long long ep_rt_core_29() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" if (strncmp(p, old_str, old_len) == 0) {\n"); + ok = append_list(lines, (long long)" memcpy(dst, new_str, new_len);\n"); + ok = append_list(lines, (long long)" dst += new_len;\n"); + ok = append_list(lines, (long long)" p += old_len;\n"); + ok = append_list(lines, (long long)" } else {\n"); + ok = append_list(lines, (long long)" *dst++ = *p++;\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" }\n"); + ok = append_list(lines, (long long)" *dst = '\\0';\n"); + ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)result;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* ========== Additional String Functions ========== */\n"); @@ -20359,17 +20381,6 @@ long long ep_rt_core_29() { ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long ep_random_int(long long min, long long max) {\n"); - ok = append_list(lines, (long long)" if (max <= min) return min;\n"); - ok = append_list(lines, (long long)" /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n"); - ok = append_list(lines, (long long)" unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n"); - ok = append_list(lines, (long long)" unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);\n"); - ok = append_list(lines, (long long)" unsigned long long r;\n"); - ok = append_list(lines, (long long)" do {\n"); - ok = append_list(lines, (long long)" ep_secure_random_bytes((unsigned char*)&r, sizeof(r));\n"); - ok = append_list(lines, (long long)" } while (r >= limit);\n"); - ok = append_list(lines, (long long)" return min + (long long)(r % range);\n"); - ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20390,6 +20401,17 @@ long long ep_rt_core_30() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" if (max <= min) return min;\n"); + ok = append_list(lines, (long long)" /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n"); + ok = append_list(lines, (long long)" unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n"); + ok = append_list(lines, (long long)" unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);\n"); + ok = append_list(lines, (long long)" unsigned long long r;\n"); + ok = append_list(lines, (long long)" do {\n"); + ok = append_list(lines, (long long)" ep_secure_random_bytes((unsigned char*)&r, sizeof(r));\n"); + ok = append_list(lines, (long long)" } while (r >= limit);\n"); + ok = append_list(lines, (long long)" return min + (long long)(r % range);\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"// JSON built-in functions\n"); ok = append_list(lines, (long long)"static const char* json_skip_ws(const char* p) {\n"); ok = append_list(lines, (long long)" while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n"); @@ -20529,17 +20551,6 @@ long long ep_rt_core_30() { ok = append_list(lines, (long long)" w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n"); ok = append_list(lines, (long long)" ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n"); ok = append_list(lines, (long long)" }\n"); - ok = append_list(lines, (long long)" for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n"); - ok = append_list(lines, (long long)" unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n"); - ok = append_list(lines, (long long)" for (int i = 0; i < 80; i++) {\n"); - ok = append_list(lines, (long long)" unsigned int f, k;\n"); - ok = append_list(lines, (long long)" if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }\n"); - ok = append_list(lines, (long long)" else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }\n"); - ok = append_list(lines, (long long)" else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }\n"); - ok = append_list(lines, (long long)" else { f = b ^ c ^ d; k = 0xCA62C1D6; }\n"); - ok = append_list(lines, (long long)" unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n"); - ok = append_list(lines, (long long)" e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n"); - ok = append_list(lines, (long long)" }\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20560,6 +20571,17 @@ long long ep_rt_core_31() { free_list(lines); lines = tmp_val; } + ok = append_list(lines, (long long)" for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n"); + ok = append_list(lines, (long long)" unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 80; i++) {\n"); + ok = append_list(lines, (long long)" unsigned int f, k;\n"); + ok = append_list(lines, (long long)" if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }\n"); + ok = append_list(lines, (long long)" else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }\n"); + ok = append_list(lines, (long long)" else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }\n"); + ok = append_list(lines, (long long)" else { f = b ^ c ^ d; k = 0xCA62C1D6; }\n"); + ok = append_list(lines, (long long)" unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n"); + ok = append_list(lines, (long long)" e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n"); + ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" free(msg);\n"); diff --git a/ep_runtime_gen.ep b/ep_runtime_gen.ep index 22a3a12..0099cdc 100644 --- a/ep_runtime_gen.ep +++ b/ep_runtime_gen.ep @@ -580,7 +580,8 @@ define ep_rt_core_3: set ok to append_list(lines and "static long long wait_task_group(long long group_ptr) {\n") set ok to append_list(lines and " EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n") set ok to append_list(lines and " if (!tg) return 0;\n") - set ok to append_list(lines and " \n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and " long long ep_wait_group_spin = 0;\n") set ok to append_list(lines and " int all_done = 0;\n") set ok to append_list(lines and " while (!all_done) {\n") set ok to append_list(lines and " all_done = 1;\n") @@ -615,11 +616,11 @@ define ep_rt_core_3: set ok to append_list(lines and " task->fut->completed = 1;\n") set ok to append_list(lines and " if (task->fut->waiting_task) {\n") set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n") - set ok to append_list(lines and " task->fut->waiting_task = NULL;\n") return join_strings(lines) define ep_rt_core_4: set lines to create_list() + set ok to append_list(lines and " task->fut->waiting_task = NULL;\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " free(task->args);\n") @@ -630,9 +631,19 @@ define ep_rt_core_4: set ok to append_list(lines and " } else {\n") set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n") set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n") - set ok to append_list(lines and " fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n") - set ok to append_list(lines and " exit(1);\n") + set ok to append_list(lines and " /* No coroutine tasks/timers/IO to drive. The futures may still be\n") + set ok to append_list(lines and " completed by detached worker THREADS (the self-hosted compiler\n") + set ok to append_list(lines and " emits thread-based async), so poll for their completion rather\n") + set ok to append_list(lines and " than declaring deadlock. Bounded so a genuinely stuck group\n") + set ok to append_list(lines and " still fails instead of hanging forever. */\n") + set ok to append_list(lines and " ep_sleep_ms(1);\n") + set ok to append_list(lines and " if (++ep_wait_group_spin > 60000) {\n") + set ok to append_list(lines and " fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n") + set ok to append_list(lines and " exit(1);\n") + set ok to append_list(lines and " }\n") + set ok to append_list(lines and " continue;\n") set ok to append_list(lines and " }\n") + set ok to append_list(lines and " ep_wait_group_spin = 0;\n") set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n") set ok to append_list(lines and " if (timeout > 0) {\n") set ok to append_list(lines and " ep_sleep_ms(timeout);\n") @@ -759,6 +770,10 @@ define ep_rt_core_4: set ok to append_list(lines and " `await async_wait_readable(fd)` suspends the calling async task until `fd` is\n") set ok to append_list(lines and " readable, letting the event loop run other tasks (e.g. another agent waiting on\n") set ok to append_list(lines and " its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a\n") + return join_strings(lines) + +define ep_rt_core_5: + set lines to create_list() set ok to append_list(lines and " oneshot read-readiness task with the loop, return the future. When fd becomes\n") set ok to append_list(lines and " readable, ep_async_wait_step re-enqueues the task; its step completes the future\n") set ok to append_list(lines and " and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n") @@ -770,10 +785,6 @@ define ep_rt_core_4: set ok to append_list(lines and " args->fut->completed = 1;\n") set ok to append_list(lines and " args->fut->value = 1;\n") set ok to append_list(lines and " if (args->fut->waiting_task) {\n") - return join_strings(lines) - -define ep_rt_core_5: - set lines to create_list() set ok to append_list(lines and " ep_task_enqueue(args->fut->waiting_task);\n") set ok to append_list(lines and " args->fut->waiting_task = NULL;\n") set ok to append_list(lines and " }\n") @@ -913,6 +924,10 @@ define ep_rt_core_5: set ok to append_list(lines and "\n") set ok to append_list(lines and "/* Thread registry for GC root scanning in multi-threaded environment */\n") set ok to append_list(lines and "#define EP_MAX_THREADS 256\n") + return join_strings(lines) + +define ep_rt_core_6: + set lines to create_list() set ok to append_list(lines and "static __thread void* volatile ep_thread_local_top = NULL;\n") set ok to append_list(lines and "static __thread void* ep_thread_local_bottom = NULL;\n") set ok to append_list(lines and "\n") @@ -924,10 +939,6 @@ define ep_rt_core_5: set ok to append_list(lines and "/* Per-thread GC root state — heap-allocated, stable across thread lifetime.\n") set ok to append_list(lines and " Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,\n") set ok to append_list(lines and " ep_gc_root_sp) in the global registry. When a thread exited, the __thread\n") - return join_strings(lines) - -define ep_rt_core_6: - set lines to create_list() set ok to append_list(lines and " storage was freed, leaving dangling pointers that ep_gc_mark would\n") set ok to append_list(lines and " dereference → segfault. Now each thread gets a heap-allocated state struct\n") set ok to append_list(lines and " that survives thread exit and is only recycled when the slot is reused. */\n") @@ -1067,6 +1078,10 @@ define ep_rt_core_6: set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n") set ok to append_list(lines and " if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n") set ok to append_list(lines and " /* Zero root count FIRST — even if ep_gc_mark races past the\n") + return join_strings(lines) + +define ep_rt_core_7: + set lines to create_list() set ok to append_list(lines and " active check, it will see sp=0 and walk no roots instead\n") set ok to append_list(lines and " of dereferencing stale __thread pointers */\n") set ok to append_list(lines and " if (ep_thread_gc_states[i]) {\n") @@ -1078,10 +1093,6 @@ define ep_rt_core_6: set ok to append_list(lines and " break;\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " }\n") - return join_strings(lines) - -define ep_rt_core_7: - set lines to create_list() set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") @@ -1221,6 +1232,10 @@ define ep_rt_core_7: set ok to append_list(lines and "static EpGCObject* ep_gc_find(void* ptr) {\n") set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint */\n") + return join_strings(lines) + +define ep_rt_core_8: + set lines to create_list() set ok to append_list(lines and " EpGCObject* obj = ep_gc_table_get(ptr);\n") set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n") set ok to append_list(lines and " return obj;\n") @@ -1232,10 +1247,6 @@ define ep_rt_core_7: set ok to append_list(lines and " resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */\n") set ok to append_list(lines and "static void ep_gc_write_barrier(void* host_ptr, long long val) {\n") set ok to append_list(lines and " if (val == 0) return;\n") - return join_strings(lines) - -define ep_rt_core_8: - set lines to create_list() set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n") set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */\n") set ok to append_list(lines and " EpGCObject* host_obj = ep_gc_table_get(host_ptr);\n") @@ -1375,6 +1386,10 @@ define ep_rt_core_8: set ok to append_list(lines and " order the compiler laid them out (a missed _regs would drop a register-only root). */\n") set ok to append_list(lines and " { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;\n") set ok to append_list(lines and " ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }\n") + return join_strings(lines) + +define ep_rt_core_9: + set lines to create_list() set ok to append_list(lines and " for (int t = 0; t < ep_num_threads; t++) {\n") set ok to append_list(lines and " if (!ep_thread_active[t]) continue;\n") set ok to append_list(lines and " if (!ep_thread_tops[t]) continue;\n") @@ -1386,10 +1401,6 @@ define ep_rt_core_8: set ok to append_list(lines and " void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);\n") set ok to append_list(lines and " void** end = (void**)ep_thread_bottoms[t];\n") set ok to append_list(lines and " if (!start || !end) continue;\n") - return join_strings(lines) - -define ep_rt_core_9: - set lines to create_list() set ok to append_list(lines and " if (start > end) { void** tmp = start; start = end; end = tmp; }\n") set ok to append_list(lines and " for (void** cur = start; cur < end; cur++) {\n") set ok to append_list(lines and " void* p = *cur;\n") @@ -1529,6 +1540,10 @@ define ep_rt_core_9: set ok to append_list(lines and " if (val != 0) {\n") set ok to append_list(lines and " ep_gc_mark_object_minor((void*)val);\n") set ok to append_list(lines and " }\n") + return join_strings(lines) + +define ep_rt_core_10: + set lines to create_list() set ok to append_list(lines and " }\n") set ok to append_list(lines and " /* Mark active tasks in the scheduler run queue for minor collection */\n") set ok to append_list(lines and " EpTask* task = ep_run_queue_head;\n") @@ -1540,10 +1555,6 @@ define ep_rt_core_9: set ok to append_list(lines and " long long* ptr = (long long*)task->args;\n") set ok to append_list(lines and " for (int i = 0; i < task->args_size_bytes / 8; i++) {\n") set ok to append_list(lines and " long long val = ptr[i];\n") - return join_strings(lines) - -define ep_rt_core_10: - set lines to create_list() set ok to append_list(lines and " if (val != 0) ep_gc_mark_object_minor((void*)val);\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " }\n") @@ -1683,6 +1694,10 @@ define ep_rt_core_10: set ok to append_list(lines and "\n") set ok to append_list(lines and "/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function\n") set ok to append_list(lines and " GC safepoint: if another thread has stopped the world, park here until it's done. */\n") + return join_strings(lines) + +define ep_rt_core_11: + set lines to create_list() set ok to append_list(lines and "static void ep_gc_maybe_collect(void) {\n") set ok to append_list(lines and " if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n") set ok to append_list(lines and " /* Safepoint: lock-free fast check, then park under the lock if a collection\n") @@ -1694,10 +1709,6 @@ define ep_rt_core_10: set ok to append_list(lines and " }\n") set ok to append_list(lines and " /* Fast path: check thresholds before acquiring mutex.\n") set ok to append_list(lines and " Counters are only incremented under the mutex, so worst case\n") - return join_strings(lines) - -define ep_rt_core_11: - set lines to create_list() set ok to append_list(lines and " we miss one collection cycle — safe trade-off for avoiding\n") set ok to append_list(lines and " a mutex lock/unlock (~20-50ns) on every function call. */\n") set ok to append_list(lines and " if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;\n") @@ -1837,6 +1848,10 @@ define ep_rt_core_11: set ok to append_list(lines and "static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "static void ep_register_channel(EpChannel* chan) {\n") + return join_strings(lines) + +define ep_rt_core_12: + set lines to create_list() set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n") set ok to append_list(lines and " if (ep_channel_count < EP_MAX_CHANNELS) {\n") set ok to append_list(lines and " ep_channel_registry[ep_channel_count++] = chan;\n") @@ -1848,10 +1863,6 @@ define ep_rt_core_11: set ok to append_list(lines and " These are defined here (after EpChannel) so they can access struct fields. */\n") set ok to append_list(lines and "static void ep_gc_mark_object(void* ptr); /* forward decl */\n") set ok to append_list(lines and "static void ep_gc_mark_object_minor(void* ptr); /* forward decl */\n") - return join_strings(lines) - -define ep_rt_core_12: - set lines to create_list() set ok to append_list(lines and "\n") set ok to append_list(lines and "static void ep_gc_scan_channels_major_impl(void) {\n") set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n") @@ -1991,6 +2002,10 @@ define ep_rt_core_12: set ok to append_list(lines and " // Poll all channels\n") set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") set ok to append_list(lines and " EpChannel* chan = (EpChannel*)list->data[i];\n") + return join_strings(lines) + +define ep_rt_core_13: + set lines to create_list() set ok to append_list(lines and " if (chan) {\n") set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") set ok to append_list(lines and " if (chan->size > 0) {\n") @@ -2002,10 +2017,6 @@ define ep_rt_core_12: set ok to append_list(lines and " }\n") set ok to append_list(lines and " \n") set ok to append_list(lines and " // Check timeout\n") - return join_strings(lines) - -define ep_rt_core_13: - set lines to create_list() set ok to append_list(lines and " if (timeout_ms >= 0) {\n") set ok to append_list(lines and "#ifdef _WIN32\n") set ok to append_list(lines and " ULONGLONG now_tick = GetTickCount64();\n") @@ -2145,6 +2156,10 @@ define ep_rt_core_13: set ok to append_list(lines and " setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n") set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") + return join_strings(lines) + +define ep_rt_core_14: + set lines to create_list() set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") set ok to append_list(lines and " serv_addr.sin_addr.s_addr = INADDR_ANY;\n") set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") @@ -2156,10 +2171,6 @@ define ep_rt_core_13: set ok to append_list(lines and "#endif\n") set ok to append_list(lines and " return -1;\n") set ok to append_list(lines and " }\n") - return join_strings(lines) - -define ep_rt_core_14: - set lines to create_list() set ok to append_list(lines and " if (listen(sockfd, 10) < 0) {\n") set ok to append_list(lines and "#ifdef _WIN32\n") set ok to append_list(lines and " closesocket(sockfd);\n") @@ -2299,6 +2310,10 @@ define ep_rt_core_14: set ok to append_list(lines and "long long ep_dlcall0(long long fptr) {\n") set ok to append_list(lines and " return ((ep_fn0)fptr)();\n") set ok to append_list(lines and "}\n") + return join_strings(lines) + +define ep_rt_core_15: + set lines to create_list() set ok to append_list(lines and "long long ep_dlcall1(long long fptr, long long a0) {\n") set ok to append_list(lines and " return ((ep_fn1)fptr)(a0);\n") set ok to append_list(lines and "}\n") @@ -2310,10 +2325,6 @@ define ep_rt_core_14: set ok to append_list(lines and "}\n") set ok to append_list(lines and "long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n") set ok to append_list(lines and " return ((ep_fn4)fptr)(a0, a1, a2, a3);\n") - return join_strings(lines) - -define ep_rt_core_15: - set lines to create_list() set ok to append_list(lines and "}\n") set ok to append_list(lines and "long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n") set ok to append_list(lines and " return ((ep_fn5)fptr)(a0, a1, a2, a3, a4);\n") @@ -2453,6 +2464,10 @@ define ep_rt_core_15: set ok to append_list(lines and " }\n") set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n") set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].key);\n") + return join_strings(lines) + +define ep_rt_core_16: + set lines to create_list() set ok to append_list(lines and " }\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and "}\n") @@ -2464,10 +2479,6 @@ define ep_rt_core_15: set ok to append_list(lines and " map->size = 0;\n") set ok to append_list(lines and " map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n") set ok to append_list(lines and " if (!map->entries) {\n") - return join_strings(lines) - -define ep_rt_core_16: - set lines to create_list() set ok to append_list(lines and " free(map);\n") set ok to append_list(lines and " return 0;\n") set ok to append_list(lines and " }\n") @@ -2607,6 +2618,10 @@ define ep_rt_core_16: set ok to append_list(lines and " char* k = map->entries[next_h].key;\n") set ok to append_list(lines and " long long v = map->entries[next_h].value;\n") set ok to append_list(lines and " map->entries[next_h].key = NULL;\n") + return join_strings(lines) + +define ep_rt_core_17: + set lines to create_list() set ok to append_list(lines and " map->entries[next_h].value = 0;\n") set ok to append_list(lines and " map->entries[next_h].used = 0;\n") set ok to append_list(lines and " map->size--;\n") @@ -2618,10 +2633,6 @@ define ep_rt_core_16: set ok to append_list(lines and " }\n") set ok to append_list(lines and " h = (h + 1) % map->capacity;\n") set ok to append_list(lines and " if (h == start_h) break;\n") - return join_strings(lines) - -define ep_rt_core_17: - set lines to create_list() set ok to append_list(lines and " }\n") set ok to append_list(lines and " return 0;\n") set ok to append_list(lines and "}\n") @@ -2761,6 +2772,10 @@ define ep_rt_core_17: set ok to append_list(lines and " free(dq->data);\n") set ok to append_list(lines and " free(dq);\n") set ok to append_list(lines and " return 0;\n") + return join_strings(lines) + +define ep_rt_core_18: + set lines to create_list() set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "/* Filesystem Operations */\n") @@ -2772,10 +2787,6 @@ define ep_rt_core_17: set ok to append_list(lines and " long long list_ptr = create_list();\n") set ok to append_list(lines and " if (!path) return list_ptr;\n") set ok to append_list(lines and " DIR* d = opendir(path);\n") - return join_strings(lines) - -define ep_rt_core_18: - set lines to create_list() set ok to append_list(lines and " if (!d) return list_ptr;\n") set ok to append_list(lines and " struct dirent* dir;\n") set ok to append_list(lines and " while ((dir = readdir(d)) != NULL) {\n") @@ -2915,6 +2926,10 @@ define ep_rt_core_18: set ok to append_list(lines and " \"Host: %s\\r\\n\"\n") set ok to append_list(lines and " \"Content-Length: %zu\\r\\n\"\n") set ok to append_list(lines and " \"Connection: close\\r\\n\"\n") + return join_strings(lines) + +define ep_rt_core_19: + set lines to create_list() set ok to append_list(lines and " \"%s%s\"\n") set ok to append_list(lines and " \"\\r\\n\",\n") set ok to append_list(lines and " method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n") @@ -2926,10 +2941,6 @@ define ep_rt_core_18: set ok to append_list(lines and " if (send(sockfd, body, body_len, 0) < 0) {\n") set ok to append_list(lines and " close(sockfd);\n") set ok to append_list(lines and " return (long long)strdup(\"Error: send body failed\");\n") - return join_strings(lines) - -define ep_rt_core_19: - set lines to create_list() set ok to append_list(lines and " }\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " size_t resp_cap = 4096;\n") @@ -3069,6 +3080,10 @@ define ep_rt_core_19: set ok to append_list(lines and " char* result = malloc(65);\n") set ok to append_list(lines and " if (result) {\n") set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n") + return join_strings(lines) + +define ep_rt_core_20: + set lines to create_list() set ok to append_list(lines and " snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " result[64] = '\\0';\n") @@ -3080,10 +3095,6 @@ define ep_rt_core_19: set ok to append_list(lines and " safe), so keys/messages containing NUL bytes hash correctly. Returns a\n") set ok to append_list(lines and " malloc'd 64-char lowercase hex string. */\n") set ok to append_list(lines and "long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {\n") - return join_strings(lines) - -define ep_rt_core_20: - set lines to create_list() set ok to append_list(lines and " const unsigned char* key = (const unsigned char*)key_ptr;\n") set ok to append_list(lines and " const unsigned char* msg = (const unsigned char*)msg_ptr;\n") set ok to append_list(lines and " size_t klen = (size_t)key_len;\n") @@ -3223,6 +3234,10 @@ define ep_rt_core_20: set ok to append_list(lines and " unsigned char bits[8];\n") set ok to append_list(lines and " bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n") set ok to append_list(lines and " bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n") + return join_strings(lines) + +define ep_rt_core_21: + set lines to create_list() set ok to append_list(lines and " unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n") set ok to append_list(lines and " unsigned char padding[64];\n") set ok to append_list(lines and " memset(padding, 0, 64); padding[0] = 0x80;\n") @@ -3234,10 +3249,6 @@ define ep_rt_core_20: set ok to append_list(lines and " digest[i*4 + 2] = ctx->state[i] >> 16;\n") set ok to append_list(lines and " digest[i*4 + 3] = ctx->state[i] >> 24;\n") set ok to append_list(lines and " }\n") - return join_strings(lines) - -define ep_rt_core_21: - set lines to create_list() set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "char* ep_md5(const char* s) {\n") @@ -3377,6 +3388,10 @@ define ep_rt_core_21: set ok to append_list(lines and "long long sqlite_get_callback_ptr(long long dummy) {\n") set ok to append_list(lines and " return (long long)sqlite_list_callback;\n") set ok to append_list(lines and "}\n") + return join_strings(lines) + +define ep_rt_core_22: + set lines to create_list() set ok to append_list(lines and "\n") set ok to append_list(lines and "/* SQLite type-safe wrappers — marshal between int and long long */\n") set ok to append_list(lines and "#ifdef EP_HAS_SQLITE\n") @@ -3388,10 +3403,6 @@ define ep_rt_core_21: set ok to append_list(lines and "long long ep_sqlite3_open(long long filename, long long db_ptr) {\n") set ok to append_list(lines and " sqlite3* db = NULL;\n") set ok to append_list(lines and " int rc = sqlite3_open((const char*)filename, &db);\n") - return join_strings(lines) - -define ep_rt_core_22: - set lines to create_list() set ok to append_list(lines and " if (rc == 0 && db_ptr != 0) {\n") set ok to append_list(lines and " *((long long*)db_ptr) = (long long)db;\n") set ok to append_list(lines and " }\n") @@ -3531,6 +3542,10 @@ define ep_rt_core_22: set ok to append_list(lines and " if (!sub) {\n") set ok to append_list(lines and " char* empty = malloc(1);\n") set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") + return join_strings(lines) + +define ep_rt_core_23: + set lines to create_list() set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") set ok to append_list(lines and " return empty;\n") set ok to append_list(lines and " }\n") @@ -3542,10 +3557,6 @@ define ep_rt_core_22: set ok to append_list(lines and "\n") set ok to append_list(lines and "char* string_from_list(long long list_ptr) {\n") set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n") - return join_strings(lines) - -define ep_rt_core_23: - set lines to create_list() set ok to append_list(lines and " if (!list) {\n") set ok to append_list(lines and " char* empty = malloc(1);\n") set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") @@ -3685,6 +3696,10 @@ define ep_rt_core_23: set ok to append_list(lines and " const char* content = (const char*)content_ptr;\n") set ok to append_list(lines and " FILE* f = fopen(path, \"ab\");\n") set ok to append_list(lines and " if (!f) return 0;\n") + return join_strings(lines) + +define ep_rt_core_24: + set lines to create_list() set ok to append_list(lines and " fputs(content, f);\n") set ok to append_list(lines and " fclose(f);\n") set ok to append_list(lines and " return 1;\n") @@ -3696,10 +3711,6 @@ define ep_rt_core_23: set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 0;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - return join_strings(lines) - -define ep_rt_core_24: - set lines to create_list() set ok to append_list(lines and "long long ep_is_directory(long long path_ptr) {\n") set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") set ok to append_list(lines and " struct stat st;\n") @@ -3839,6 +3850,10 @@ define ep_rt_core_24: set ok to append_list(lines and " return val ? (long long)val : (long long)\"\";\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") + return join_strings(lines) + +define ep_rt_core_25: + set lines to create_list() set ok to append_list(lines and "long long ep_setenv(long long name_ptr, long long val_ptr) {\n") set ok to append_list(lines and " return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n") set ok to append_list(lines and "}\n") @@ -3850,10 +3865,6 @@ define ep_rt_core_24: set ok to append_list(lines and " return (long long)\"\";\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - return join_strings(lines) - -define ep_rt_core_25: - set lines to create_list() set ok to append_list(lines and "long long ep_os_name(void) {\n") set ok to append_list(lines and " #if defined(__APPLE__)\n") set ok to append_list(lines and " return (long long)\"macos\";\n") @@ -3993,6 +4004,10 @@ define ep_rt_core_25: set ok to append_list(lines and " SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n") set ok to append_list(lines and " InitializeSRWLock(rwl);\n") set ok to append_list(lines and " return (long long)rwl;\n") + return join_strings(lines) + +define ep_rt_core_26: + set lines to create_list() set ok to append_list(lines and "}\n") set ok to append_list(lines and "long long ep_rwlock_read_lock(long long rwl) {\n") set ok to append_list(lines and " AcquireSRWLockShared((SRWLOCK*)rwl);\n") @@ -4004,10 +4019,6 @@ define ep_rt_core_25: set ok to append_list(lines and "}\n") set ok to append_list(lines and "long long ep_rwlock_unlock(long long rwl) {\n") set ok to append_list(lines and " /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n") - return join_strings(lines) - -define ep_rt_core_26: - set lines to create_list() set ok to append_list(lines and " In practice the caller should know which lock was taken.\n") set ok to append_list(lines and " ReleaseSRWLockExclusive on a shared lock is undefined, but\n") set ok to append_list(lines and " the runtime guarantees matched lock/unlock pairs. We default\n") @@ -4147,6 +4158,10 @@ define ep_rt_core_26: set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "/* Semaphore via mutex+condvar (portable) */\n") + return join_strings(lines) + +define ep_rt_core_27: + set lines to create_list() set ok to append_list(lines and "typedef struct {\n") set ok to append_list(lines and " pthread_mutex_t mutex;\n") set ok to append_list(lines and " pthread_cond_t cond;\n") @@ -4158,10 +4173,6 @@ define ep_rt_core_26: set ok to append_list(lines and " pthread_mutex_init(&s->mutex, NULL);\n") set ok to append_list(lines and " pthread_cond_init(&s->cond, NULL);\n") set ok to append_list(lines and " s->value = initial;\n") - return join_strings(lines) - -define ep_rt_core_27: - set lines to create_list() set ok to append_list(lines and " return (long long)s;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") @@ -4301,6 +4312,10 @@ define ep_rt_core_27: set ok to append_list(lines and " memcpy(result + match.rm_so, repl, rlen);\n") set ok to append_list(lines and " memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\n") set ok to append_list(lines and " result[new_len] = '\\0';\n") + return join_strings(lines) + +define ep_rt_core_28: + set lines to create_list() set ok to append_list(lines and " regfree(®ex);\n") set ok to append_list(lines and " return (long long)result;\n") set ok to append_list(lines and "}\n") @@ -4312,10 +4327,6 @@ define ep_rt_core_27: set ok to append_list(lines and " regmatch_t match;\n") set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n") set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n") - return join_strings(lines) - -define ep_rt_core_28: - set lines to create_list() set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n") set ok to append_list(lines and " if (ret) {\n") set ok to append_list(lines and " append_list(list, text_ptr);\n") @@ -4455,6 +4466,10 @@ define ep_rt_core_28: set ok to append_list(lines and " char* dst = result;\n") set ok to append_list(lines and " p = s;\n") set ok to append_list(lines and " while (*p) {\n") + return join_strings(lines) + +define ep_rt_core_29: + set lines to create_list() set ok to append_list(lines and " if (strncmp(p, old_str, old_len) == 0) {\n") set ok to append_list(lines and " memcpy(dst, new_str, new_len);\n") set ok to append_list(lines and " dst += new_len;\n") @@ -4466,10 +4481,6 @@ define ep_rt_core_28: set ok to append_list(lines and " *dst = '\\0';\n") set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n") set ok to append_list(lines and " return (long long)result;\n") - return join_strings(lines) - -define ep_rt_core_29: - set lines to create_list() set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "/* ========== Additional String Functions ========== */\n") @@ -4609,6 +4620,10 @@ define ep_rt_core_29: set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "long long ep_random_int(long long min, long long max) {\n") + return join_strings(lines) + +define ep_rt_core_30: + set lines to create_list() set ok to append_list(lines and " if (max <= min) return min;\n") set ok to append_list(lines and " /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n") set ok to append_list(lines and " unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n") @@ -4620,10 +4635,6 @@ define ep_rt_core_29: set ok to append_list(lines and " return min + (long long)(r % range);\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - return join_strings(lines) - -define ep_rt_core_30: - set lines to create_list() set ok to append_list(lines and "// JSON built-in functions\n") set ok to append_list(lines and "static const char* json_skip_ws(const char* p) {\n") set ok to append_list(lines and " while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n") @@ -4763,6 +4774,10 @@ define ep_rt_core_30: set ok to append_list(lines and " w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n") set ok to append_list(lines and " ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n") set ok to append_list(lines and " }\n") + return join_strings(lines) + +define ep_rt_core_31: + set lines to create_list() set ok to append_list(lines and " for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n") set ok to append_list(lines and " unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n") set ok to append_list(lines and " for (int i = 0; i < 80; i++) {\n") @@ -4774,10 +4789,6 @@ define ep_rt_core_30: set ok to append_list(lines and " unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n") set ok to append_list(lines and " e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n") set ok to append_list(lines and " }\n") - return join_strings(lines) - -define ep_rt_core_31: - set lines to create_list() set ok to append_list(lines and " h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " free(msg);\n") diff --git a/runtime/ep_runtime.c b/runtime/ep_runtime.c index 1480e60..cd69b7e 100644 --- a/runtime/ep_runtime.c +++ b/runtime/ep_runtime.c @@ -562,7 +562,8 @@ static long long add_task_group(long long group_ptr, long long fut_ptr) { static long long wait_task_group(long long group_ptr) { EpTaskGroup* tg = (EpTaskGroup*)group_ptr; if (!tg) return 0; - + + long long ep_wait_group_spin = 0; int all_done = 0; while (!all_done) { all_done = 1; @@ -608,9 +609,19 @@ static long long wait_task_group(long long group_ptr) { } else { long long timeout = ep_get_next_timer_timeout(); if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) { - fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n"); - exit(1); + /* No coroutine tasks/timers/IO to drive. The futures may still be + completed by detached worker THREADS (the self-hosted compiler + emits thread-based async), so poll for their completion rather + than declaring deadlock. Bounded so a genuinely stuck group + still fails instead of hanging forever. */ + ep_sleep_ms(1); + if (++ep_wait_group_spin > 60000) { + fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n"); + exit(1); + } + continue; } + ep_wait_group_spin = 0; if (ep_event_loop_fd == -1) { if (timeout > 0) { ep_sleep_ms(timeout); From 54b028e93ba3c91a3c4e65f59e306df811a7104b Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 19:34:48 +0100 Subject: [PATCH 41/51] docs: self-hosted parity 53/54 (only nondeterministic async_loop remains) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b47ef1e..13e9dfa 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ the same generational GC, pointer-safe accessors, and OOM-guarded allocators. `epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh` (currently 52/54 runnable programs; 8/9 +tracked by `tests/run_epc_parity.sh` (currently 53/54 runnable programs; 8/9 compile-error tests correctly rejected by the `ep_check.ep` semantic pass). `epc check ` runs the checks without codegen. From 850386576858e835db3164c1bc3a4f4be1a6e64c Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 20:05:33 +0100 Subject: [PATCH 42/51] =?UTF-8?q?feat(self-host):=20coroutine=20(event-loo?= =?UTF-8?q?p)=20async=20=E2=80=94=20deterministic=20ordering=20(async=5Flo?= =?UTF-8?q?op=20passes)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the self-hosted thread-based async with the Rust compiler's coroutine state-machine model, reusing the shared runtime's scheduler: - async fn -> args struct (state + future + persisted params/locals + awaited futures), a _step function (switch on state), and a public wrapper that enqueues a task and returns the future. - await inside async -> yields (saves state, registers waiting_task, returns -999999) via emit_async_yields, resuming at the case label; value read from the persisted awaited_fut slot. await outside async -> ep_await_future drives the event loop. - In a step body, params/locals are rewritten to args->name and return is direct. count_awaits/emit_async_yields added; state slots 19-21. sleep_ms already returns a timer-future, so yields and the single-threaded event loop interleaves workers deterministically — test_async_loop now matches the golden ordering. test_task_group still passes on the coroutine scheduler. Gates: FIXPOINT OK; async_loop + task_group pass; run_tests.sh 69/69; bootstrap regenerated. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 480 ++++++++++++++++++++++++++++---------- ep_codegen.ep | 360 +++++++++++++++++++--------- 2 files changed, 603 insertions(+), 237 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index cb81ab8..dd0a09c 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -5016,6 +5016,10 @@ long long cg_int_to_str(long long); long long escape_string(long long); long long join_strings(long long); long long create_codegen_state(); +long long count_awaits_expr(long long); +long long count_awaits_stmts(long long); +long long emit_async_yields_expr(long long, long long, long long, long long); +long long emit_async_yields_stmt(long long, long long, long long, long long); long long type_name_to_code(long long); long long is_builtin_c_func(long long, long long); long long get_codegen_borrowed_keys(long long); @@ -10962,6 +10966,9 @@ long long create_codegen_state() { ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, (create_list() + 0LL)); + ok = append_list(state, 0LL); + ok = append_list(state, 0LL); + ok = append_list(state, (create_list() + 0LL)); ret_val = state; state = 0; goto L_cleanup; @@ -10970,6 +10977,180 @@ long long create_codegen_state() { return ret_val; } +long long count_awaits_expr(long long expr) { + long long t = 0; + long long args = 0; + long long c = 0; + long long i = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + if (expr == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + t = get_list(expr, 0LL); + if (t == 21LL) { + ret_val = (1LL + count_awaits_expr(get_list(expr, 1LL))); + goto L_cleanup; + } + if (((t == 4LL || t == 5LL) || t == 14LL)) { + ret_val = (count_awaits_expr(get_list(expr, 1LL)) + count_awaits_expr(get_list(expr, 3LL))); + goto L_cleanup; + } + if ((((t == 20LL || t == 32LL) || t == 33LL) || t == 18LL)) { + ret_val = count_awaits_expr(get_list(expr, 1LL)); + goto L_cleanup; + } + if (t == 6LL) { + args = get_list(expr, 2LL); + c = 0LL; + i = 0LL; + while (i < length_list(args)) { + c = (c + count_awaits_expr(get_list(args, i))); + i = (i + 1LL); + } + ret_val = c; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long count_awaits_stmts(long long stmts) { + long long c = 0; + long long i = 0; + long long stmt = 0; + long long t = 0; + long long eb = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + c = 0LL; + i = 0LL; + while (i < length_list(stmts)) { + stmt = get_list(stmts, i); + t = get_list(stmt, 0LL); + if (t == 7LL) { + c = (c + count_awaits_expr(get_list(stmt, 2LL))); + } + if (((t == 8LL || t == 9LL) || t == 36LL)) { + c = (c + count_awaits_expr(get_list(stmt, 1LL))); + } + if (t == 10LL) { + c = (c + count_awaits_expr(get_list(stmt, 1LL))); + c = (c + count_awaits_stmts(get_list(stmt, 2LL))); + eb = get_list(stmt, 3LL); + if (eb != 0LL) { + c = (c + count_awaits_stmts(eb)); + } + } + if (t == 11LL) { + c = (c + count_awaits_expr(get_list(stmt, 1LL))); + c = (c + count_awaits_stmts(get_list(stmt, 2LL))); + } + if (t == 28LL) { + c = (c + count_awaits_stmts(get_list(stmt, 3LL))); + } + i = (i + 1LL); + } + ret_val = c; + goto L_cleanup; +L_cleanup: + return ret_val; +} + +long long emit_async_yields_expr(long long state, long long expr, long long var_keys, long long var_values) { + long long t = 0; + long long ok = 0; + long long n = 0; + long long ns = 0; + long long inner_str = 0; + long long line = 0; + long long args = 0; + long long i = 0; + long long ret_val = 0; + + ep_gc_push_root(&inner_str); + ep_gc_push_root(&line); + ep_gc_maybe_collect(); + + if (expr == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + t = get_list(expr, 0LL); + if (t == 21LL) { + ok = emit_async_yields_expr(state, get_list(expr, 1LL), var_keys, var_values); + n = (get_list(state, 20LL) + 1LL); + ok = set_list(state, 20LL, n); + ns = cg_int_to_str(n); + inner_str = gen_expr(state, get_list(expr, 1LL), var_keys, var_values); + line = (long long)" { EpFuture* _f = (EpFuture*)("; + line = string_concat(line, inner_str); + line = string_concat(line, (long long)"); args->awaited_fut_"); + line = string_concat(line, ns); + line = string_concat(line, (long long)" = _f; if (_f && !_f->completed) { args->state = "); + line = string_concat(line, ns); + line = string_concat(line, (long long)"; _f->waiting_task = ep_current_task; return -999999; } }\n case "); + line = string_concat(line, ns); + line = string_concat(line, (long long)":\n"); + ok = emit(state, line); + ret_val = 0LL; + goto L_cleanup; + } + if (((t == 4LL || t == 5LL) || t == 14LL)) { + ok = emit_async_yields_expr(state, get_list(expr, 1LL), var_keys, var_values); + ok = emit_async_yields_expr(state, get_list(expr, 3LL), var_keys, var_values); + ret_val = 0LL; + goto L_cleanup; + } + if ((((t == 20LL || t == 32LL) || t == 33LL) || t == 18LL)) { + ret_val = emit_async_yields_expr(state, get_list(expr, 1LL), var_keys, var_values); + goto L_cleanup; + } + if (t == 6LL) { + args = get_list(expr, 2LL); + i = 0LL; + while (i < length_list(args)) { + ok = emit_async_yields_expr(state, get_list(args, i), var_keys, var_values); + i = (i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(2); + return ret_val; +} + +long long emit_async_yields_stmt(long long state, long long stmt, long long var_keys, long long var_values) { + long long t = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + t = get_list(stmt, 0LL); + if (t == 7LL) { + ret_val = emit_async_yields_expr(state, get_list(stmt, 2LL), var_keys, var_values); + goto L_cleanup; + } + if (((t == 8LL || t == 9LL) || t == 36LL)) { + ret_val = emit_async_yields_expr(state, get_list(stmt, 1LL), var_keys, var_values); + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + long long type_name_to_code(long long tname) { long long t = 0; long long ret_val = 0; @@ -12005,10 +12186,22 @@ long long gen_function(long long state, long long func) { long long p_name = 0; long long is_borrow = 0; long long param_type = 0; - long long struct_decl = 0; - long long j = 0; - long long wrap_fn = 0; - long long pub_fn = 0; + long long cname = 0; + long long aw_count = 0; + long long async_locals = 0; + long long al_i = 0; + long long okal = 0; + long long sd = 0; + long long vi = 0; + long long awi = 0; + long long sf = 0; + long long bs_i = 0; + long long bstmt = 0; + long long saved_ctr = 0; + long long post_ctr = 0; + long long pf = 0; + long long pj = 0; + long long pnm = 0; long long impl_name = 0; long long header = 0; long long borrowed_keys = 0; @@ -12030,10 +12223,10 @@ long long gen_function(long long state, long long func) { long long root_pop = 0; long long ret_val = 0; - ep_gc_push_root(&struct_decl); - ep_gc_push_root(&wrap_fn); - ep_gc_push_root(&pub_fn); - ep_gc_push_root(&impl_name); + ep_gc_push_root(&async_locals); + ep_gc_push_root(&sd); + ep_gc_push_root(&sf); + ep_gc_push_root(&pf); ep_gc_push_root(&header); ep_gc_push_root(&decl); ep_gc_push_root(&root_line); @@ -12068,104 +12261,114 @@ long long gen_function(long long state, long long func) { } ok = collect_var_types(state, body, var_types_keys, var_types_values); if (is_async == 1LL) { - struct_decl = (long long)"typedef struct {\n EpFuture* fut;\n"; - j = 0LL; - while (j < p_len) { - struct_decl = string_concat(struct_decl, (long long)" long long arg"); - struct_decl = string_concat(struct_decl, cg_int_to_str(j)); - struct_decl = string_concat(struct_decl, (long long)";\n"); - j = (j + 1LL); - } - if (p_len == 0LL) { - struct_decl = string_concat(struct_decl, (long long)" int dummy;\n"); - } - struct_decl = string_concat(struct_decl, (long long)"} "); - struct_decl = string_concat(struct_decl, get_fn_c_name(func)); - struct_decl = string_concat(struct_decl, (long long)"_async_args;\n\n"); - ok = emit(state, struct_decl); - wrap_fn = (long long)"void* "; - wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); - wrap_fn = string_concat(wrap_fn, (long long)"_async_wrapper(void* r) {\n"); - wrap_fn = string_concat(wrap_fn, (long long)" int stack_dummy;\n"); - wrap_fn = string_concat(wrap_fn, (long long)" ep_gc_register_thread(&stack_dummy);\n"); - wrap_fn = string_concat(wrap_fn, (long long)" "); - wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); - wrap_fn = string_concat(wrap_fn, (long long)"_async_args* args = ("); - wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); - wrap_fn = string_concat(wrap_fn, (long long)"_async_args*)r;\n"); - wrap_fn = string_concat(wrap_fn, (long long)" long long res = "); - wrap_fn = string_concat(wrap_fn, get_fn_c_name(func)); - wrap_fn = string_concat(wrap_fn, (long long)"_impl("); - j = 0LL; - while (j < p_len) { - wrap_fn = string_concat(wrap_fn, (long long)"args->arg"); - wrap_fn = string_concat(wrap_fn, cg_int_to_str(j)); - if (j < (p_len - 1LL)) { - wrap_fn = string_concat(wrap_fn, (long long)", "); - } - j = (j + 1LL); - } - wrap_fn = string_concat(wrap_fn, (long long)");\n"); - wrap_fn = string_concat(wrap_fn, (long long)" args->fut->value = res;\n"); - wrap_fn = string_concat(wrap_fn, (long long)" args->fut->completed = 1;\n"); - wrap_fn = string_concat(wrap_fn, (long long)" send_channel(args->fut->chan, res);\n"); - wrap_fn = string_concat(wrap_fn, (long long)" free(args);\n"); - wrap_fn = string_concat(wrap_fn, (long long)" ep_gc_unregister_thread();\n"); - wrap_fn = string_concat(wrap_fn, (long long)" return NULL;\n"); - wrap_fn = string_concat(wrap_fn, (long long)"}\n\n"); - ok = emit(state, wrap_fn); - pub_fn = (long long)"long long "; - pub_fn = string_concat(pub_fn, get_fn_c_name(func)); - pub_fn = string_concat(pub_fn, (long long)"("); - j = 0LL; - while (j < p_len) { - p_node = get_list(params, j); - p_name = get_list(p_node, 0LL); - pub_fn = string_concat(pub_fn, (long long)"long long "); - pub_fn = string_concat(pub_fn, p_name); - if (j < (p_len - 1LL)) { - pub_fn = string_concat(pub_fn, (long long)", "); - } - j = (j + 1LL); - } - pub_fn = string_concat(pub_fn, (long long)") {\n"); - pub_fn = string_concat(pub_fn, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n"); - pub_fn = string_concat(pub_fn, (long long)" fut->chan = create_channel();\n"); - pub_fn = string_concat(pub_fn, (long long)" fut->completed = 0;\n"); - pub_fn = string_concat(pub_fn, (long long)" fut->value = 0;\n"); - pub_fn = string_concat(pub_fn, (long long)" ep_gc_register(fut, EP_OBJ_STRUCT);\n"); - pub_fn = string_concat(pub_fn, (long long)" "); - pub_fn = string_concat(pub_fn, get_fn_c_name(func)); - pub_fn = string_concat(pub_fn, (long long)"_async_args* args = ("); - pub_fn = string_concat(pub_fn, get_fn_c_name(func)); - pub_fn = string_concat(pub_fn, (long long)"_async_args*)malloc(sizeof("); - pub_fn = string_concat(pub_fn, get_fn_c_name(func)); - pub_fn = string_concat(pub_fn, (long long)"_async_args));\n"); - pub_fn = string_concat(pub_fn, (long long)" args->fut = fut;\n"); - j = 0LL; - while (j < p_len) { - p_node = get_list(params, j); - p_name = get_list(p_node, 0LL); - pub_fn = string_concat(pub_fn, (long long)" args->arg"); - pub_fn = string_concat(pub_fn, cg_int_to_str(j)); - pub_fn = string_concat(pub_fn, (long long)" = "); - pub_fn = string_concat(pub_fn, p_name); - pub_fn = string_concat(pub_fn, (long long)";\n"); - j = (j + 1LL); + cname = get_fn_c_name(func); + aw_count = count_awaits_stmts(body); + { + long long tmp_val = create_list(); + free_list(async_locals); + async_locals = tmp_val; + } + al_i = 0LL; + while (al_i < length_list(var_types_keys)) { + okal = append_list(async_locals, get_list(var_types_keys, al_i)); + al_i = (al_i + 1LL); + } + sd = (long long)"typedef struct {\n int state;\n EpFuture* fut;\n"; + vi = 0LL; + while (vi < length_list(var_types_keys)) { + sd = string_concat(sd, (long long)" long long "); + sd = string_concat(sd, get_list(var_types_keys, vi)); + sd = string_concat(sd, (long long)";\n"); + vi = (vi + 1LL); + } + awi = 1LL; + while (awi <= aw_count) { + sd = string_concat(sd, (long long)" EpFuture* awaited_fut_"); + sd = string_concat(sd, cg_int_to_str(awi)); + sd = string_concat(sd, (long long)";\n"); + awi = (awi + 1LL); + } + if (length_list(var_types_keys) == 0LL) { + if (aw_count == 0LL) { + sd = string_concat(sd, (long long)" int dummy;\n"); + } + } + sd = string_concat(sd, (long long)"} "); + sd = string_concat(sd, cname); + sd = string_concat(sd, (long long)"_async_args;\n\n"); + ok = emit(state, sd); + sf = (long long)"long long "; + sf = string_concat(sf, cname); + sf = string_concat(sf, (long long)"_step(void* r) {\n "); + sf = string_concat(sf, cname); + sf = string_concat(sf, (long long)"_async_args* args = ("); + sf = string_concat(sf, cname); + sf = string_concat(sf, (long long)"_async_args*)r;\n switch (args->state) {\n case 0:\n"); + ok = emit(state, sf); + ok = set_list(state, 19LL, 1LL); + ok = set_list(state, 21LL, async_locals); + ok = set_list(state, 20LL, 0LL); + ok = set_codegen_borrowed_keys(state, (create_list() + 0LL)); + ok = set_codegen_borrowed_values(state, (create_list() + 0LL)); + bs_i = 0LL; + while (bs_i < length_list(body)) { + bstmt = get_list(body, bs_i); + saved_ctr = get_list(state, 20LL); + ok = emit_async_yields_stmt(state, bstmt, var_types_keys, var_types_values); + post_ctr = get_list(state, 20LL); + ok = set_list(state, 20LL, saved_ctr); + ok = gen_statement(state, bstmt, var_types_keys, var_types_values); + ok = set_list(state, 20LL, post_ctr); + bs_i = (bs_i + 1LL); } - pub_fn = string_concat(pub_fn, (long long)" pthread_t thread;\n"); - pub_fn = string_concat(pub_fn, (long long)" pthread_create(&thread, NULL, "); - pub_fn = string_concat(pub_fn, get_fn_c_name(func)); - pub_fn = string_concat(pub_fn, (long long)"_async_wrapper, args);\n"); - pub_fn = string_concat(pub_fn, (long long)" pthread_detach(thread);\n"); - pub_fn = string_concat(pub_fn, (long long)" return (long long)fut;\n"); - pub_fn = string_concat(pub_fn, (long long)"}\n\n"); - ok = emit(state, pub_fn); + ok = emit(state, (long long)" args->state = -1;\n return 0;\n }\n return 0;\n}\n\n"); + ok = set_list(state, 19LL, 0LL); + pf = (long long)"long long "; + pf = string_concat(pf, cname); + pf = string_concat(pf, (long long)"("); + pj = 0LL; + while (pj < p_len) { + pf = string_concat(pf, (long long)"long long "); + pf = string_concat(pf, get_list(get_list(params, pj), 0LL)); + if (pj < (p_len - 1LL)) { + pf = string_concat(pf, (long long)", "); + } + pj = (pj + 1LL); + } + pf = string_concat(pf, (long long)") {\n"); + pf = string_concat(pf, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n"); + pf = string_concat(pf, (long long)" fut->completed = 0; fut->value = 0; fut->waiting_task = NULL; fut->chan = 0;\n"); + pf = string_concat(pf, (long long)" ep_gc_register(fut, EP_OBJ_STRUCT);\n"); + pf = string_concat(pf, (long long)" "); + pf = string_concat(pf, cname); + pf = string_concat(pf, (long long)"_async_args* args = ("); + pf = string_concat(pf, cname); + pf = string_concat(pf, (long long)"_async_args*)malloc(sizeof("); + pf = string_concat(pf, cname); + pf = string_concat(pf, (long long)"_async_args));\n memset(args, 0, sizeof("); + pf = string_concat(pf, cname); + pf = string_concat(pf, (long long)"_async_args));\n args->state = 0;\n args->fut = fut;\n"); + pj = 0LL; + while (pj < p_len) { + pnm = get_list(get_list(params, pj), 0LL); + pf = string_concat(pf, (long long)" args->"); + pf = string_concat(pf, pnm); + pf = string_concat(pf, (long long)" = "); + pf = string_concat(pf, pnm); + pf = string_concat(pf, (long long)";\n"); + pj = (pj + 1LL); + } + pf = string_concat(pf, (long long)" EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n"); + pf = string_concat(pf, (long long)" task->step = "); + pf = string_concat(pf, cname); + pf = string_concat(pf, (long long)"_step;\n task->args = args;\n task->args_size_bytes = sizeof("); + pf = string_concat(pf, cname); + pf = string_concat(pf, (long long)"_async_args);\n task->fut = fut;\n task->state = 0;\n task->is_cancelled = 0;\n task->parent = ep_current_task;\n ep_task_enqueue(task);\n return (long long)fut;\n}\n\n"); + ok = emit(state, pf); + ret_val = 0LL; + goto L_cleanup; } impl_name = get_fn_c_name(func); - if (is_async == 1LL) { - impl_name = string_concat(get_fn_c_name(func), (long long)"_impl"); - } header = (long long)"long long "; header = string_concat(header, impl_name); header = string_concat(header, (long long)"("); @@ -12287,12 +12490,14 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon long long expr = 0; long long t = 0; long long expr_str = 0; + long long al = 0; + long long ok = 0; long long is_global = 0; long long borrowed_keys = 0; long long borrowed_values = 0; long long is_borrowed = 0; - long long ok = 0; long long line = 0; + long long arl = 0; long long expr_type = 0; long long null_line = 0; long long is_any_call = 0; @@ -12354,7 +12559,9 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon long long ret_val = 0; ep_gc_push_root(&expr_str); + ep_gc_push_root(&al); ep_gc_push_root(&line); + ep_gc_push_root(&arl); ep_gc_push_root(&null_line); ep_gc_push_root(&callee); ep_gc_push_root(&cond_str); @@ -12374,6 +12581,18 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon expr = get_list(stmt, 2LL); t = map_get(var_keys, var_values, name); expr_str = gen_expr(state, expr, var_keys, var_values); + if (get_list(state, 19LL) == 1LL) { + if (contains_string_val(get_list(state, 21LL), name) == 1LL) { + al = (long long)" args->"; + al = string_concat(al, name); + al = string_concat(al, (long long)" = "); + al = string_concat(al, expr_str); + al = string_concat(al, (long long)";\n"); + ok = emit(state, al); + ret_val = 0LL; + goto L_cleanup; + } + } is_global = is_global_var(name); borrowed_keys = get_codegen_borrowed_keys(state); borrowed_values = get_codegen_borrowed_values(state); @@ -12407,6 +12626,14 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon if (type == 8LL) { expr = get_list(stmt, 1LL); expr_str = gen_expr(state, expr, var_keys, var_values); + if (get_list(state, 19LL) == 1LL) { + arl = (long long)" return "; + arl = string_concat(arl, expr_str); + arl = string_concat(arl, (long long)";\n"); + ok = emit(state, arl); + ret_val = 0LL; + goto L_cleanup; + } line = (long long)" ret_val = "; line = string_concat(line, expr_str); line = string_concat(line, (long long)";\n"); @@ -12776,7 +13003,7 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon ret_val = 0LL; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(13); + ep_gc_pop_roots(15); return ret_val; } @@ -12825,6 +13052,8 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon long long chan = 0; long long chan_str = 0; long long inner = 0; + long long awn = 0; + long long awns = 0; long long inner_str = 0; long long obj = 0; long long field_name = 0; @@ -12976,6 +13205,12 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon } if (type == 3LL) { name = get_list(expr, 1LL); + if (get_list(state, 19LL) == 1LL) { + if (contains_string_val(get_list(state, 21LL), name) == 1LL) { + ret_val = string_concat((long long)"args->", name); + goto L_cleanup; + } + } if (map_contains_key(var_keys, name) == 0LL) { if (map_contains_key(get_list(state, 12LL), name) == 1LL) { eres = (long long)"({ long long* _v = (long long*)malloc(sizeof(long long) * 1); _v[0] = EP_TAG_"; @@ -13294,10 +13529,22 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon } if (type == 21LL) { inner = get_list(expr, 1LL); + if (get_list(state, 19LL) == 1LL) { + awn = (get_list(state, 20LL) + 1LL); + ok = set_list(state, 20LL, awn); + awns = cg_int_to_str(awn); + res = (long long)"(args->awaited_fut_"; + res = string_concat(res, awns); + res = string_concat(res, (long long)" ? args->awaited_fut_"); + res = string_concat(res, awns); + res = string_concat(res, (long long)"->value : 0)"); + ret_val = res; + goto L_cleanup; + } inner_str = gen_expr(state, inner, var_keys, var_values); - res = (long long)"({ EpFuture* _fut = (EpFuture*)"; + res = (long long)"ep_await_future((EpFuture*)"; res = string_concat(res, inner_str); - res = string_concat(res, (long long)"; long long _res = 0; if (_fut) { if (!_fut->completed) { _res = receive_channel(_fut->chan); } else { _res = _fut->value; } } _res; })"); + res = string_concat(res, (long long)")"); ret_val = res; goto L_cleanup; } @@ -14739,7 +14986,6 @@ long long generate_c(long long program, long long is_test_mode) { long long func = 0; long long is_async = 0; long long proto2 = 0; - long long proto3 = 0; long long spawn_list = 0; long long dummy = 0; long long spawn_len = 0; @@ -14807,7 +15053,6 @@ long long generate_c(long long program, long long is_test_mode) { ep_gc_push_root(&il); ep_gc_push_root(&proto); ep_gc_push_root(&proto2); - ep_gc_push_root(&proto3); ep_gc_push_root(&spawn_list); ep_gc_push_root(&struct_decl); ep_gc_push_root(&wrap_fn); @@ -15109,21 +15354,8 @@ long long generate_c(long long program, long long is_test_mode) { if (is_async == 1LL) { proto2 = (long long)"long long "; proto2 = string_concat(proto2, get_fn_c_name(func)); - proto2 = string_concat(proto2, (long long)"_impl("); - p_i = 0LL; - while (p_i < p_len) { - proto2 = string_concat(proto2, (long long)"long long"); - if (p_i < (p_len - 1LL)) { - proto2 = string_concat(proto2, (long long)", "); - } - p_i = (p_i + 1LL); - } - proto2 = string_concat(proto2, (long long)");\n"); + proto2 = string_concat(proto2, (long long)"_step(void* r);\n"); ok = emit(state, proto2); - proto3 = (long long)"void* "; - proto3 = string_concat(proto3, get_fn_c_name(func)); - proto3 = string_concat(proto3, (long long)"_async_wrapper(void* r);\n"); - ok = emit(state, proto3); } idx = (idx + 1LL); } @@ -15284,7 +15516,7 @@ long long generate_c(long long program, long long is_test_mode) { ret_val = c_code; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(22); + ep_gc_pop_roots(21); return ret_val; } diff --git a/ep_codegen.ep b/ep_codegen.ep index b6943ed..91fc4dc 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -362,8 +362,105 @@ define create_codegen_state: set ok to append_list(state and (create_list() + 0)) # vpat_keys (16): variant name set ok to append_list(state and (create_list() + 0)) # vpat_vals (17): list of field type-codes for that variant set ok to append_list(state and (create_list() + 0)) # iter_structs (18): struct names implementing the Iterator trait + set ok to append_list(state and 0) # is_async (19): 1 while emitting an async function's step body + set ok to append_list(state and 0) # await_counter (20): running await index within the current async fn + set ok to append_list(state and (create_list() + 0)) # async_locals (21): names rewritten to args-> in the step body return state +# Count await expressions in a statement list / expression (each becomes a +# state in the coroutine step machine, with a persisted awaited_fut_N slot). +define count_awaits_expr with expr: + if expr == 0: + return 0 + set t to get_list(expr and 0) + if t == 21: # NODE_AWAIT + return 1 + count_awaits_expr(get_list(expr and 1)) + if t == 4 || t == 5 || t == 14: # BINARY/COMP/LOGICAL + return count_awaits_expr(get_list(expr and 1)) + count_awaits_expr(get_list(expr and 3)) + if t == 20 || t == 32 || t == 33 || t == 18: # BORROW/NOT/TRY/RECEIVE + return count_awaits_expr(get_list(expr and 1)) + if t == 6: # CALL + set args to get_list(expr and 2) + set c to 0 + set i to 0 + repeat while i < length_list(args): + set c to c + count_awaits_expr(get_list(args and i)) + set i to i + 1 + return c + return 0 + +define count_awaits_stmts with stmts: + set c to 0 + set i to 0 + repeat while i < length_list(stmts): + set stmt to get_list(stmts and i) + set t to get_list(stmt and 0) + if t == 7: # SET + set c to c + count_awaits_expr(get_list(stmt and 2)) + if t == 8 || t == 9 || t == 36: # RETURN/DISPLAY/EXPR_STMT + set c to c + count_awaits_expr(get_list(stmt and 1)) + if t == 10: # IF + set c to c + count_awaits_expr(get_list(stmt and 1)) + set c to c + count_awaits_stmts(get_list(stmt and 2)) + set eb to get_list(stmt and 3) + if eb != 0: + set c to c + count_awaits_stmts(eb) + if t == 11: # WHILE + set c to c + count_awaits_expr(get_list(stmt and 1)) + set c to c + count_awaits_stmts(get_list(stmt and 2)) + if t == 28: # FOR_EACH + set c to c + count_awaits_stmts(get_list(stmt and 3)) + set i to i + 1 + return c + +# Emit the coroutine yield boilerplate for each await in an expression, in +# evaluation order, advancing the await counter (state slot 20). Mirrors the +# Rust emit_yields_for_expr. `case N:` resumes here once the awaited future +# completes. +define emit_async_yields_expr with state and expr and var_keys and var_values: + if expr == 0: + return 0 + set t to get_list(expr and 0) + if t == 21: # NODE_AWAIT + set ok to emit_async_yields_expr(state and get_list(expr and 1) and var_keys and var_values) + set n to get_list(state and 20) + 1 + set ok to set_list(state and 20 and n) + set ns to cg_int_to_str(n) + set inner_str to gen_expr(state and get_list(expr and 1) and var_keys and var_values) + set line to " { EpFuture* _f = (EpFuture*)(" + set line to string_concat(line and inner_str) + set line to string_concat(line and "); args->awaited_fut_") + set line to string_concat(line and ns) + set line to string_concat(line and " = _f; if (_f && !_f->completed) { args->state = ") + set line to string_concat(line and ns) + set line to string_concat(line and "; _f->waiting_task = ep_current_task; return -999999; } }\n case ") + set line to string_concat(line and ns) + set line to string_concat(line and ":\n") + set ok to emit(state and line) + return 0 + if t == 4 || t == 5 || t == 14: + set ok to emit_async_yields_expr(state and get_list(expr and 1) and var_keys and var_values) + set ok to emit_async_yields_expr(state and get_list(expr and 3) and var_keys and var_values) + return 0 + if t == 20 || t == 32 || t == 33 || t == 18: + return emit_async_yields_expr(state and get_list(expr and 1) and var_keys and var_values) + if t == 6: # CALL + set args to get_list(expr and 2) + set i to 0 + repeat while i < length_list(args): + set ok to emit_async_yields_expr(state and get_list(args and i) and var_keys and var_values) + set i to i + 1 + return 0 + return 0 + +define emit_async_yields_stmt with state and stmt and var_keys and var_values: + set t to get_list(stmt and 0) + if t == 7: # SET + return emit_async_yields_expr(state and get_list(stmt and 2) and var_keys and var_values) + if t == 8 || t == 9 || t == 36: # RETURN/DISPLAY/EXPR_STMT + return emit_async_yields_expr(state and get_list(stmt and 1) and var_keys and var_values) + return 0 + # Map an ErnosPlain type-annotation name to the codegen's coarse type code. define type_name_to_code with tname: set t to string_concat(tname and "") @@ -977,101 +1074,110 @@ define gen_function with state and func: set ok to collect_var_types(state and body and var_types_keys and var_types_values) if is_async == 1: - # 1. Thread argument struct - set struct_decl to "typedef struct {\n EpFuture* fut;\n" - set j to 0 - repeat while j < p_len: - set struct_decl to string_concat(struct_decl and " long long arg") - set struct_decl to string_concat(struct_decl and cg_int_to_str(j)) - set struct_decl to string_concat(struct_decl and ";\n") - set j to j + 1 - if p_len == 0: - set struct_decl to string_concat(struct_decl and " int dummy;\n") - set struct_decl to string_concat(struct_decl and "} ") - set struct_decl to string_concat(struct_decl and get_fn_c_name(func)) - set struct_decl to string_concat(struct_decl and "_async_args;\n\n") - set ok to emit(state and struct_decl) - - # 2. Thread wrapper function - set wrap_fn to "void* " - set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func)) - set wrap_fn to string_concat(wrap_fn and "_async_wrapper(void* r) {\n") - set wrap_fn to string_concat(wrap_fn and " int stack_dummy;\n") - set wrap_fn to string_concat(wrap_fn and " ep_gc_register_thread(&stack_dummy);\n") - set wrap_fn to string_concat(wrap_fn and " ") - set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func)) - set wrap_fn to string_concat(wrap_fn and "_async_args* args = (") - set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func)) - set wrap_fn to string_concat(wrap_fn and "_async_args*)r;\n") - set wrap_fn to string_concat(wrap_fn and " long long res = ") - set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func)) - set wrap_fn to string_concat(wrap_fn and "_impl(") - set j to 0 - repeat while j < p_len: - set wrap_fn to string_concat(wrap_fn and "args->arg") - set wrap_fn to string_concat(wrap_fn and cg_int_to_str(j)) - if j < p_len - 1: - set wrap_fn to string_concat(wrap_fn and ", ") - set j to j + 1 - set wrap_fn to string_concat(wrap_fn and ");\n") - set wrap_fn to string_concat(wrap_fn and " args->fut->value = res;\n") - set wrap_fn to string_concat(wrap_fn and " args->fut->completed = 1;\n") - set wrap_fn to string_concat(wrap_fn and " send_channel(args->fut->chan, res);\n") - set wrap_fn to string_concat(wrap_fn and " free(args);\n") - set wrap_fn to string_concat(wrap_fn and " ep_gc_unregister_thread();\n") - set wrap_fn to string_concat(wrap_fn and " return NULL;\n") - set wrap_fn to string_concat(wrap_fn and "}\n\n") - set ok to emit(state and wrap_fn) - - # 3. Public wrapper function - set pub_fn to "long long " - set pub_fn to string_concat(pub_fn and get_fn_c_name(func)) - set pub_fn to string_concat(pub_fn and "(") - set j to 0 - repeat while j < p_len: - set p_node to get_list(params and j) - set p_name to get_list(p_node and 0) - set pub_fn to string_concat(pub_fn and "long long ") - set pub_fn to string_concat(pub_fn and p_name) - if j < p_len - 1: - set pub_fn to string_concat(pub_fn and ", ") - set j to j + 1 - set pub_fn to string_concat(pub_fn and ") {\n") - set pub_fn to string_concat(pub_fn and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n") - set pub_fn to string_concat(pub_fn and " fut->chan = create_channel();\n") - set pub_fn to string_concat(pub_fn and " fut->completed = 0;\n") - set pub_fn to string_concat(pub_fn and " fut->value = 0;\n") - set pub_fn to string_concat(pub_fn and " ep_gc_register(fut, EP_OBJ_STRUCT);\n") - set pub_fn to string_concat(pub_fn and " ") - set pub_fn to string_concat(pub_fn and get_fn_c_name(func)) - set pub_fn to string_concat(pub_fn and "_async_args* args = (") - set pub_fn to string_concat(pub_fn and get_fn_c_name(func)) - set pub_fn to string_concat(pub_fn and "_async_args*)malloc(sizeof(") - set pub_fn to string_concat(pub_fn and get_fn_c_name(func)) - set pub_fn to string_concat(pub_fn and "_async_args));\n") - set pub_fn to string_concat(pub_fn and " args->fut = fut;\n") - set j to 0 - repeat while j < p_len: - set p_node to get_list(params and j) - set p_name to get_list(p_node and 0) - set pub_fn to string_concat(pub_fn and " args->arg") - set pub_fn to string_concat(pub_fn and cg_int_to_str(j)) - set pub_fn to string_concat(pub_fn and " = ") - set pub_fn to string_concat(pub_fn and p_name) - set pub_fn to string_concat(pub_fn and ";\n") - set j to j + 1 - set pub_fn to string_concat(pub_fn and " pthread_t thread;\n") - set pub_fn to string_concat(pub_fn and " pthread_create(&thread, NULL, ") - set pub_fn to string_concat(pub_fn and get_fn_c_name(func)) - set pub_fn to string_concat(pub_fn and "_async_wrapper, args);\n") - set pub_fn to string_concat(pub_fn and " pthread_detach(thread);\n") - set pub_fn to string_concat(pub_fn and " return (long long)fut;\n") - set pub_fn to string_concat(pub_fn and "}\n\n") - set ok to emit(state and pub_fn) - + set cname to get_fn_c_name(func) + set aw_count to count_awaits_stmts(body) + # async_locals: params + body locals (all live in the args struct) + set async_locals to create_list() + set al_i to 0 + repeat while al_i < length_list(var_types_keys): + set okal to append_list(async_locals and get_list(var_types_keys and al_i)) + set al_i to al_i + 1 + + # 1. Coroutine args struct (state + future + persisted params/locals + awaited futures) + set sd to "typedef struct {\n int state;\n EpFuture* fut;\n" + set vi to 0 + repeat while vi < length_list(var_types_keys): + set sd to string_concat(sd and " long long ") + set sd to string_concat(sd and get_list(var_types_keys and vi)) + set sd to string_concat(sd and ";\n") + set vi to vi + 1 + set awi to 1 + repeat while awi <= aw_count: + set sd to string_concat(sd and " EpFuture* awaited_fut_") + set sd to string_concat(sd and cg_int_to_str(awi)) + set sd to string_concat(sd and ";\n") + set awi to awi + 1 + if length_list(var_types_keys) == 0: + if aw_count == 0: + set sd to string_concat(sd and " int dummy;\n") + set sd to string_concat(sd and "} ") + set sd to string_concat(sd and cname) + set sd to string_concat(sd and "_async_args;\n\n") + set ok to emit(state and sd) + + # 2. Step function + set sf to "long long " + set sf to string_concat(sf and cname) + set sf to string_concat(sf and "_step(void* r) {\n ") + set sf to string_concat(sf and cname) + set sf to string_concat(sf and "_async_args* args = (") + set sf to string_concat(sf and cname) + set sf to string_concat(sf and "_async_args*)r;\n switch (args->state) {\n case 0:\n") + set ok to emit(state and sf) + + # Body in async mode: mark context, empty borrow maps, per-statement + # yield emission then generation (counter re-walked in lockstep). + set ok to set_list(state and 19 and 1) + set ok to set_list(state and 21 and async_locals) + set ok to set_list(state and 20 and 0) + set ok to set_codegen_borrowed_keys(state and (create_list() + 0)) + set ok to set_codegen_borrowed_values(state and (create_list() + 0)) + set bs_i to 0 + repeat while bs_i < length_list(body): + set bstmt to get_list(body and bs_i) + set saved_ctr to get_list(state and 20) + set ok to emit_async_yields_stmt(state and bstmt and var_types_keys and var_types_values) + set post_ctr to get_list(state and 20) + set ok to set_list(state and 20 and saved_ctr) + set ok to gen_statement(state and bstmt and var_types_keys and var_types_values) + set ok to set_list(state and 20 and post_ctr) + set bs_i to bs_i + 1 + set ok to emit(state and " args->state = -1;\n return 0;\n }\n return 0;\n}\n\n") + set ok to set_list(state and 19 and 0) + + # 3. Public wrapper: build the future + task and enqueue it. + set pf to "long long " + set pf to string_concat(pf and cname) + set pf to string_concat(pf and "(") + set pj to 0 + repeat while pj < p_len: + set pf to string_concat(pf and "long long ") + set pf to string_concat(pf and get_list(get_list(params and pj) and 0)) + if pj < p_len - 1: + set pf to string_concat(pf and ", ") + set pj to pj + 1 + set pf to string_concat(pf and ") {\n") + set pf to string_concat(pf and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n") + set pf to string_concat(pf and " fut->completed = 0; fut->value = 0; fut->waiting_task = NULL; fut->chan = 0;\n") + set pf to string_concat(pf and " ep_gc_register(fut, EP_OBJ_STRUCT);\n") + set pf to string_concat(pf and " ") + set pf to string_concat(pf and cname) + set pf to string_concat(pf and "_async_args* args = (") + set pf to string_concat(pf and cname) + set pf to string_concat(pf and "_async_args*)malloc(sizeof(") + set pf to string_concat(pf and cname) + set pf to string_concat(pf and "_async_args));\n memset(args, 0, sizeof(") + set pf to string_concat(pf and cname) + set pf to string_concat(pf and "_async_args));\n args->state = 0;\n args->fut = fut;\n") + set pj to 0 + repeat while pj < p_len: + set pnm to get_list(get_list(params and pj) and 0) + set pf to string_concat(pf and " args->") + set pf to string_concat(pf and pnm) + set pf to string_concat(pf and " = ") + set pf to string_concat(pf and pnm) + set pf to string_concat(pf and ";\n") + set pj to pj + 1 + set pf to string_concat(pf and " EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n") + set pf to string_concat(pf and " task->step = ") + set pf to string_concat(pf and cname) + set pf to string_concat(pf and "_step;\n task->args = args;\n task->args_size_bytes = sizeof(") + set pf to string_concat(pf and cname) + set pf to string_concat(pf and "_async_args);\n task->fut = fut;\n task->state = 0;\n task->is_cancelled = 0;\n task->parent = ep_current_task;\n ep_task_enqueue(task);\n return (long long)fut;\n}\n\n") + set ok to emit(state and pf) + return 0 + set impl_name to get_fn_c_name(func) - if is_async == 1: - set impl_name to string_concat(get_fn_c_name(func) and "_impl") # Build function header: long long impl_name(long long p1, long long p2, ...) set header to "long long " @@ -1197,9 +1303,21 @@ define gen_statement with state and stmt and var_keys and var_values: set name to get_list(stmt and 1) set expr to get_list(stmt and 2) set t to map_get(var_keys and var_values and name) - + set expr_str to gen_expr(state and expr and var_keys and var_values) - + + # In an async step body, locals live in the args struct (persist across + # yields); emit a plain assignment to args->name (no decl / no free). + if get_list(state and 19) == 1: + if contains_string_val(get_list(state and 21) and name) == 1: + set al to " args->" + set al to string_concat(al and name) + set al to string_concat(al and " = ") + set al to string_concat(al and expr_str) + set al to string_concat(al and ";\n") + set ok to emit(state and al) + return 0 + set is_global to is_global_var(name) set borrowed_keys to get_codegen_borrowed_keys(state) set borrowed_values to get_codegen_borrowed_values(state) @@ -1236,7 +1354,15 @@ define gen_statement with state and stmt and var_keys and var_values: if type == 8: # NODE_RETURN set expr to get_list(stmt and 1) set expr_str to gen_expr(state and expr and var_keys and var_values) - + # In an async step body, return the value directly (the wrapper completes + # the future via the scheduler); no L_cleanup / ret_val machinery. + if get_list(state and 19) == 1: + set arl to " return " + set arl to string_concat(arl and expr_str) + set arl to string_concat(arl and ";\n") + set ok to emit(state and arl) + return 0 + set line to " ret_val = " set line to string_concat(line and expr_str) set line to string_concat(line and ";\n") @@ -1610,6 +1736,10 @@ define gen_expr with state and expr and var_keys and var_values: if type == 3: # NODE_IDENT set name to get_list(expr and 1) + # Inside an async step body, params and locals live in the args struct. + if get_list(state and 19) == 1: + if contains_string_val(get_list(state and 21) and name) == 1: + return string_concat("args->" and name) # A bare enum variant (e.g. `North`) constructs the zero-payload # variant. Local variables shadow variant names. if map_contains_key(var_keys and name) == 0: @@ -1885,10 +2015,24 @@ define gen_expr with state and expr and var_keys and var_values: if type == 21: # NODE_AWAIT set inner to get_list(expr and 1) + # Inside an async step, the await already yielded (see emit_async_yields); + # the value is read from the persisted awaited_fut slot at the resumed + # state. Counter is re-walked here in lockstep with emit_async_yields. + if get_list(state and 19) == 1: + set awn to get_list(state and 20) + 1 + set ok to set_list(state and 20 and awn) + set awns to cg_int_to_str(awn) + set res to "(args->awaited_fut_" + set res to string_concat(res and awns) + set res to string_concat(res and " ? args->awaited_fut_") + set res to string_concat(res and awns) + set res to string_concat(res and "->value : 0)") + return res + # Non-async caller: drive the event loop until the future completes. set inner_str to gen_expr(state and inner and var_keys and var_values) - set res to "({ EpFuture* _fut = (EpFuture*)" + set res to "ep_await_future((EpFuture*)" set res to string_concat(res and inner_str) - set res to string_concat(res and "; long long _res = 0; if (_fut) { if (!_fut->completed) { _res = receive_channel(_fut->chan); } else { _res = _fut->value; } } _res; })") + set res to string_concat(res and ")") return res if type == 22: # NODE_FIELD_ACCESS @@ -3048,23 +3192,13 @@ define generate_c with program and is_test_mode: set ok to emit(state and proto) if is_async == 1: + # Coroutine step-function prototype (the public wrapper is declared + # above like any function). set proto2 to "long long " set proto2 to string_concat(proto2 and get_fn_c_name(func)) - set proto2 to string_concat(proto2 and "_impl(") - set p_i to 0 - repeat while p_i < p_len: - set proto2 to string_concat(proto2 and "long long") - if p_i < p_len - 1: - set proto2 to string_concat(proto2 and ", ") - set p_i to p_i + 1 - set proto2 to string_concat(proto2 and ");\n") + set proto2 to string_concat(proto2 and "_step(void* r);\n") set ok to emit(state and proto2) - - set proto3 to "void* " - set proto3 to string_concat(proto3 and get_fn_c_name(func)) - set proto3 to string_concat(proto3 and "_async_wrapper(void* r);\n") - set ok to emit(state and proto3) - + set idx to idx + 1 set ok to emit(state and "\n") From 4720841f21acfba5bb7cd2532d1986909f4a29e9 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Thu, 2 Jul 2026 20:15:04 +0100 Subject: [PATCH 43/51] =?UTF-8?q?docs:=20self-hosted=20parity=2054/54=20?= =?UTF-8?q?=E2=80=94=20full=20runnable=20suite=20passes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit epc now compiles and passes every runnable test the Rust compiler does (54/54, stable across 8 harness runs), enforces 8/9 compile-error rejections, reaches a byte-identical fixpoint, and builds with no Rust via the frozen bootstrap. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 13e9dfa..4f24c47 100644 --- a/README.md +++ b/README.md @@ -345,14 +345,15 @@ the same generational GC, pointer-safe accessors, and OOM-guarded allocators. `epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh` (currently 53/54 runnable programs; 8/9 +tracked by `tests/run_epc_parity.sh` (currently 54/54 runnable programs; 8/9 compile-error tests correctly rejected by the `ep_check.ep` semantic pass). `epc check ` runs the checks without codegen. The self-hosted pipeline is `lex → parse → check → optimize → codegen` -(`ep_lexer` · `ep_parser` · `ep_check` · `ep_optimizer` · `ep_codegen`). The -remaining coverage gap is trait vtables, struct float fields, and full enum -return-type inference; the Rust compiler covers those plus the LSP and +(`ep_lexer` · `ep_parser` · `ep_check` · `ep_optimizer` · `ep_codegen`), covering +the full runnable test suite — closures, floats, traits + the iterator protocol, +`try`/Result, coroutine async, globals, and the English-alias surface. The Rust +compiler additionally provides the LSP and cross-language transpilers. ### Fully self-contained bootstrap (no Rust required) From 08d100e087b7e6d492719a80e85c8aafed78dd50 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 02:42:27 +0100 Subject: [PATCH 44/51] fix(self-host): enum field-type check no longer triggers use-after-free (54/54 + 9/9, zero caveats) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-hosted semantic checker gained enum-variant field-type checking so test_enum_type is correctly rejected (rejections 8/9 -> 9/9). That exposed two distinct heap-use-after-frees that only surfaced on enum-heavy programs (test_errors, test_types, test_enum_method): 1. ep_check.ep bound a shared sub-list to a local: `en_field_types` returned `get_list(vf, i)` (a list aliased inside en_vf), which the Rust codegen's cleanup pass then free_list'd at function exit, corrupting en_vf. Replaced it with `en_field_type_at`, which reads the shared vf sub-lists strictly inline and returns a freshly-built string — no list-typed local is ever aliased. 2. ep_codegen.ep's list-reassignment path emitted `free_list(old)` before the new value. For a per-iteration `fts` that had already been appended into en_vf, that freed a value the container still owned -> use-after-free on the next iteration. Removed the auto-free (mirroring the earlier cleanup-free removal); the generational GC reclaims genuinely-unreachable lists. Verified ASAN-clean under BOTH the Rust-built and the clang-only bootstrapped epc. Gates: parity 54/54 runnable, 9 rejected / 0 wrongly accepted, 3-stage fixpoint byte-identical, bootstrap/verify.sh (Rust-free) green, run_tests.sh 69/69, zero cargo warnings. Frozen bootstrap regenerated in the same commit. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 1172 ++++++++++++++----------------------- ep_check.ep | 150 +++++ ep_codegen.ep | 44 +- 3 files changed, 589 insertions(+), 777 deletions(-) diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index dd0a09c..322d7f2 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -4998,6 +4998,10 @@ long long check_lit_category(long long); long long check_expr(long long, long long, long long); long long check_stmts(long long, long long); long long check_function(long long, long long); +long long en_arg_type(long long, long long, long long); +long long en_field_type_at(long long, long long, long long, long long); +long long en_check_expr(long long, long long, long long, long long, long long); +long long en_check_stmts(long long, long long, long long, long long, long long); long long check_program(long long); long long opt_fold_expr(long long); long long opt_fold_stmts(long long); @@ -5345,11 +5349,7 @@ long long parse_all_modules(long long current_file, long long parsed_files, long ret_val = 1LL; goto L_cleanup; } - { - long long tmp_val = create_parser_state(tokens); - free_list(state); - state = tmp_val; - } + state = create_parser_state(tokens); program_ast = parse_program(state); if (get_parser_error(state) == 1LL) { printf("%s\n", (char*)(long long)"Compiler Error: Parsing failed in:"); @@ -5443,16 +5443,8 @@ long long parse_all_modules(long long current_file, long long parsed_files, long goto L_cleanup; } } else { - { - long long tmp_val = create_list(); - free_list(mod_funcs); - mod_funcs = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(mod_externals); - mod_externals = tmp_val; - } + mod_funcs = create_list(); + mod_externals = create_list(); status = parse_all_modules(resolved_path, parsed_files, mod_funcs, mod_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants); if (status != 0LL) { ret_val = status; @@ -5598,61 +5590,21 @@ long long _main() { } stem = get_file_stem(input_path); printf("%s\n", (char*)(long long)"[1/3] Tokenizing and Parsing..."); - { - long long tmp_val = create_list(); - free_list(all_functions); - all_functions = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(all_externals); - all_externals = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(all_struct_defs); - all_struct_defs = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(all_enum_defs); - all_enum_defs = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(all_method_defs); - all_method_defs = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(all_trait_defs); - all_trait_defs = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(all_trait_impls); - all_trait_impls = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(all_constants); - all_constants = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(parsed_files); - parsed_files = tmp_val; - } + all_functions = create_list(); + all_externals = create_list(); + all_struct_defs = create_list(); + all_enum_defs = create_list(); + all_method_defs = create_list(); + all_trait_defs = create_list(); + all_trait_impls = create_list(); + all_constants = create_list(); + parsed_files = create_list(); status = parse_all_modules(input_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants); if (status != 0LL) { ret_val = 1LL; goto L_cleanup; } - { - long long tmp_val = create_list(); - free_list(f_names); - f_names = tmp_val; - } + f_names = create_list(); all_len = length_list(all_functions); idx = 0LL; duplicate_found = 0LL; @@ -5672,16 +5624,8 @@ long long _main() { ret_val = 1LL; goto L_cleanup; } - { - long long tmp_val = create_list(); - free_list(empty_imports); - empty_imports = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(program_ast); - program_ast = tmp_val; - } + empty_imports = create_list(); + program_ast = create_list(); ok = append_list(program_ast, 13LL); ok = append_list(program_ast, empty_imports); ok = append_list(program_ast, all_externals); @@ -5774,11 +5718,7 @@ long long create_token(long long type, long long value, long long line, long lon ep_gc_push_root(&tok); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(tok); - tok = tmp_val; - } + tok = create_list(); ok = append_list(tok, type); ok = append_list(tok, value); ok = append_list(tok, line); @@ -5967,11 +5907,7 @@ long long lex_string_body(long long source, long long pos0, long long source_len pos = (pos0 + 1LL); col = (start_col + 1LL); opens = 0LL; - { - long long tmp_val = create_list(); - free_list(str_chars); - str_chars = tmp_val; - } + str_chars = create_list(); closed = 0LL; err = 0LL; looping = 1LL; @@ -5987,11 +5923,7 @@ long long lex_string_body(long long source, long long pos0, long long source_len pos = (pos + 1LL); col = (col + 1LL); lit = (long long)string_from_list(str_chars); - { - long long tmp_val = create_list(); - free_list(str_chars); - str_chars = tmp_val; - } + str_chars = create_list(); if (string_length((char*)lit) != 0LL) { t1 = (create_token(27LL, (long long)"concat", current_line, col) + 0LL); ok1 = append_list(tokens, t1); @@ -6012,11 +5944,7 @@ long long lex_string_body(long long source, long long pos0, long long source_len t8 = (create_token(23LL, (long long)"(", current_line, col) + 0LL); ok8 = append_list(tokens, t8); depth = 1LL; - { - long long tmp_val = create_list(); - free_list(expr_chars); - expr_chars = tmp_val; - } + expr_chars = create_list(); eloop = 1LL; while ((pos < source_len && eloop == 1LL)) { ec = get_character((char*)source, pos); @@ -6170,11 +6098,7 @@ long long lex_string_body(long long source, long long pos0, long long source_len } } } - { - long long tmp_val = create_list(); - free_list(res); - res = tmp_val; - } + res = create_list(); okr1 = append_list(res, pos); okr2 = append_list(res, col); okr3 = append_list(res, err); @@ -6266,20 +6190,12 @@ long long tokenize_source(long long source) { ep_gc_push_root(&num_str); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(tokens); - tokens = tmp_val; - } + tokens = create_list(); source_len = string_length((char*)source); pos = 0LL; current_line = 1LL; current_col = 1LL; - { - long long tmp_val = create_list(); - free_list(indent_stack); - indent_stack = tmp_val; - } + indent_stack = create_list(); ok = append_list(indent_stack, 0LL); at_line_start = 1LL; while (pos < source_len) { @@ -6997,11 +6913,7 @@ long long make_node_int(long long val) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 1LL); ok = append_list(node, val); ret_val = node; @@ -7020,11 +6932,7 @@ long long make_node_str(long long val) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 2LL); ok = append_list(node, val); ret_val = node; @@ -7043,11 +6951,7 @@ long long make_node_ident(long long name) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 3LL); ok = append_list(node, name); ret_val = node; @@ -7066,11 +6970,7 @@ long long make_node_binary(long long left, long long op, long long right) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 4LL); ok = append_list(node, left); ok = append_list(node, op); @@ -7091,11 +6991,7 @@ long long make_node_comp(long long left, long long op, long long right) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 5LL); ok = append_list(node, left); ok = append_list(node, op); @@ -7116,11 +7012,7 @@ long long make_node_call(long long name, long long args) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 6LL); ok = append_list(node, name); ok = append_list(node, args); @@ -7140,11 +7032,7 @@ long long make_node_set(long long var, long long expr) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 7LL); ok = append_list(node, var); ok = append_list(node, expr); @@ -7164,11 +7052,7 @@ long long make_node_return(long long expr) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 8LL); ok = append_list(node, expr); ret_val = node; @@ -7187,11 +7071,7 @@ long long make_node_display(long long expr) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 9LL); ok = append_list(node, expr); ret_val = node; @@ -7210,11 +7090,7 @@ long long make_node_if(long long cond, long long then_b, long long else_b) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 10LL); ok = append_list(node, cond); ok = append_list(node, then_b); @@ -7235,11 +7111,7 @@ long long make_node_repeat_while(long long cond, long long body) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 11LL); ok = append_list(node, cond); ok = append_list(node, body); @@ -7259,11 +7131,7 @@ long long make_node_func(long long name, long long params, long long body, long ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 12LL); ok = append_list(node, name); ok = append_list(node, params); @@ -7285,11 +7153,7 @@ long long make_node_program(long long imports, long long externals, long long fu ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 13LL); ok = append_list(node, imports); ok = append_list(node, externals); @@ -7316,11 +7180,7 @@ long long make_node_spawn(long long func_name, long long args) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 15LL); ok = append_list(node, func_name); ok = append_list(node, args); @@ -7340,11 +7200,7 @@ long long make_node_send(long long chan, long long val) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 16LL); ok = append_list(node, chan); ok = append_list(node, val); @@ -7364,11 +7220,7 @@ long long make_node_channel() { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 17LL); ret_val = node; node = 0; @@ -7386,11 +7238,7 @@ long long make_node_receive(long long chan) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 18LL); ok = append_list(node, chan); ret_val = node; @@ -7409,11 +7257,7 @@ long long make_node_external(long long name, long long params, long long ret_typ ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 19LL); ok = append_list(node, name); ok = append_list(node, params); @@ -7434,11 +7278,7 @@ long long make_node_borrow(long long target) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 20LL); ok = append_list(node, target); ret_val = node; @@ -7457,11 +7297,7 @@ long long make_node_await(long long target) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 21LL); ok = append_list(node, target); ret_val = node; @@ -7480,11 +7316,7 @@ long long make_node_logical(long long left, long long op, long long right) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 14LL); ok = append_list(node, left); ok = append_list(node, op); @@ -7505,11 +7337,7 @@ long long make_node_field_access(long long obj, long long field_name) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 22LL); ok = append_list(node, obj); ok = append_list(node, field_name); @@ -7529,11 +7357,7 @@ long long make_node_field_set(long long obj, long long field_name, long long val ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 23LL); ok = append_list(node, obj); ok = append_list(node, field_name); @@ -7554,11 +7378,7 @@ long long make_node_struct_create(long long struct_name, long long fields) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 24LL); ok = append_list(node, struct_name); ok = append_list(node, fields); @@ -7578,11 +7398,7 @@ long long make_node_method_call(long long obj, long long method_name, long long ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 25LL); ok = append_list(node, obj); ok = append_list(node, method_name); @@ -7603,11 +7419,7 @@ long long make_node_enum_create(long long variant_name, long long args) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 26LL); ok = append_list(node, variant_name); ok = append_list(node, args); @@ -7627,11 +7439,7 @@ long long make_node_match(long long expr, long long arms) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 27LL); ok = append_list(node, expr); ok = append_list(node, arms); @@ -7651,11 +7459,7 @@ long long make_node_for_each(long long var_name, long long iter_expr, long long ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 28LL); ok = append_list(node, var_name); ok = append_list(node, iter_expr); @@ -7676,11 +7480,7 @@ long long make_node_break() { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 29LL); ret_val = node; node = 0; @@ -7698,11 +7498,7 @@ long long make_node_continue() { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 30LL); ret_val = node; node = 0; @@ -7720,11 +7516,7 @@ long long make_node_bool(long long val) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 31LL); ok = append_list(node, val); ret_val = node; @@ -7743,11 +7535,7 @@ long long make_node_unary_not(long long expr) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 32LL); ok = append_list(node, expr); ret_val = node; @@ -7766,11 +7554,7 @@ long long make_node_try(long long expr) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 33LL); ok = append_list(node, expr); ret_val = node; @@ -7789,11 +7573,7 @@ long long make_node_closure(long long params, long long body) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 34LL); ok = append_list(node, params); ok = append_list(node, body); @@ -7813,11 +7593,7 @@ long long make_node_list_lit(long long elements) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 35LL); ok = append_list(node, elements); ret_val = node; @@ -7836,11 +7612,7 @@ long long make_node_expr_stmt(long long expr) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 36LL); ok = append_list(node, expr); ret_val = node; @@ -7859,11 +7631,7 @@ long long make_node_struct_def(long long name, long long fields) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 37LL); ok = append_list(node, name); ok = append_list(node, fields); @@ -7883,11 +7651,7 @@ long long make_node_enum_def(long long name, long long variants) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 38LL); ok = append_list(node, name); ok = append_list(node, variants); @@ -7907,11 +7671,7 @@ long long make_node_method_def(long long method_name, long long struct_name, lon ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 39LL); ok = append_list(node, method_name); ok = append_list(node, struct_name); @@ -7933,11 +7693,7 @@ long long make_node_trait_def(long long name, long long method_sigs) { ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 40LL); ok = append_list(node, name); ok = append_list(node, method_sigs); @@ -7957,11 +7713,7 @@ long long make_node_trait_impl(long long trait_name, long long type_name, long l ep_gc_push_root(&node); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(node); - node = tmp_val; - } + node = create_list(); ok = append_list(node, 41LL); ok = append_list(node, trait_name); ok = append_list(node, type_name); @@ -7982,11 +7734,7 @@ long long create_parser_state(long long tokens) { ep_gc_push_root(&state); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(state); - state = tmp_val; - } + state = create_list(); ok = append_list(state, tokens); ok = append_list(state, 0LL); ok = append_list(state, 0LL); @@ -10001,8 +9749,240 @@ long long check_function(long long func, long long errs) { return ret_val; } +long long en_arg_type(long long arg, long long vk, long long vo) { + long long t = 0; + long long vn = 0; + long long i = 0; + long long ret_val = 0; + + ep_gc_push_root(&vn); + ep_gc_maybe_collect(); + + if (arg == 0LL) { + ret_val = (long long)""; + goto L_cleanup; + } + t = get_list(arg, 0LL); + if (t == 26LL) { + vn = string_concat(get_list(arg, 1LL), (long long)""); + i = 0LL; + while (i < length_list(vk)) { + if ((strcmp((char*)vn, (char*)get_list(vk, i)) == 0)) { + ret_val = get_list(vo, i); + goto L_cleanup; + } + i = (i + 1LL); + } + ret_val = (long long)""; + goto L_cleanup; + } + if (t == 1LL) { + ret_val = (long long)"Int"; + goto L_cleanup; + } + if (t == 2LL) { + ret_val = (long long)"Str"; + goto L_cleanup; + } + if (t == 42LL) { + ret_val = (long long)"Float"; + goto L_cleanup; + } + if (t == 31LL) { + ret_val = (long long)"Bool"; + goto L_cleanup; + } + ret_val = (long long)""; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long en_field_type_at(long long variant, long long ai, long long vk, long long vf) { + long long vn = 0; + long long i = 0; + long long ret_val = 0; + + ep_gc_push_root(&vn); + ep_gc_maybe_collect(); + + vn = string_concat(variant, (long long)""); + i = 0LL; + while (i < length_list(vk)) { + if ((strcmp((char*)vn, (char*)get_list(vk, i)) == 0)) { + if (ai < length_list(get_list(vf, i))) { + ret_val = string_concat(get_list(get_list(vf, i), ai), (long long)""); + goto L_cleanup; + } + ret_val = (long long)""; + goto L_cleanup; + } + i = (i + 1LL); + } + ret_val = (long long)""; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(1); + return ret_val; +} + +long long en_check_expr(long long expr, long long errs, long long vk, long long vo, long long vf) { + long long t = 0; + long long variant = 0; + long long args = 0; + long long ai = 0; + long long arg = 0; + long long ft = 0; + long long at = 0; + long long noop = 0; + long long msg = 0; + long long ok = 0; + long long oka = 0; + long long okl = 0; + long long okr = 0; + long long cargs = 0; + long long ci = 0; + long long okc = 0; + long long ret_val = 0; + + ep_gc_push_root(&ft); + ep_gc_push_root(&at); + ep_gc_push_root(&msg); + ep_gc_maybe_collect(); + + if (expr == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + t = get_list(expr, 0LL); + if (t == 26LL) { + variant = get_list(expr, 1LL); + args = get_list(expr, 2LL); + ai = 0LL; + while (ai < length_list(args)) { + arg = get_list(args, ai); + ft = string_concat(en_field_type_at(variant, ai, vk, vf), (long long)""); + if (string_length((char*)ft) > 0LL) { + at = string_concat(en_arg_type(arg, vk, vo), (long long)""); + if (string_length((char*)at) > 0LL) { + if ((strcmp((char*)at, (char*)ft) == 0)) { + noop = 0LL; + } else { + msg = string_concat((long long)"enum variant field type mismatch: expected ", ft); + msg = string_concat(msg, (long long)" but got "); + msg = string_concat(msg, at); + ok = append_list(errs, msg); + } + } + } + oka = en_check_expr(arg, errs, vk, vo, vf); + ai = (ai + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + if (((t == 4LL || t == 5LL) || t == 14LL)) { + okl = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf); + okr = en_check_expr(get_list(expr, 3LL), errs, vk, vo, vf); + ret_val = 0LL; + goto L_cleanup; + } + if (t == 6LL) { + cargs = get_list(expr, 2LL); + ci = 0LL; + while (ci < length_list(cargs)) { + okc = en_check_expr(get_list(cargs, ci), errs, vk, vo, vf); + ci = (ci + 1LL); + } + ret_val = 0LL; + goto L_cleanup; + } + if (((((t == 18LL || t == 20LL) || t == 21LL) || t == 32LL) || t == 33LL)) { + ret_val = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf); + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(3); + return ret_val; +} + +long long en_check_stmts(long long stmts, long long errs, long long vk, long long vo, long long vf) { + long long i = 0; + long long stmt = 0; + long long t = 0; + long long ok = 0; + long long eb = 0; + long long arms = 0; + long long ari = 0; + long long ret_val = 0; + + ep_gc_maybe_collect(); + + i = 0LL; + while (i < length_list(stmts)) { + stmt = get_list(stmts, i); + t = get_list(stmt, 0LL); + if (t == 7LL) { + ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf); + } + if (((t == 8LL || t == 9LL) || t == 36LL)) { + ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf); + } + if (t == 10LL) { + ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf); + ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf); + eb = get_list(stmt, 3LL); + if (eb != 0LL) { + ok = en_check_stmts(eb, errs, vk, vo, vf); + } + } + if (t == 11LL) { + ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf); + ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf); + } + if (t == 28LL) { + ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf); + ok = en_check_stmts(get_list(stmt, 3LL), errs, vk, vo, vf); + } + if (t == 27LL) { + arms = get_list(stmt, 2LL); + ari = 0LL; + while (ari < length_list(arms)) { + ok = en_check_stmts(get_list(get_list(arms, ari), 2LL), errs, vk, vo, vf); + ari = (ari + 1LL); + } + } + i = (i + 1LL); + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + return ret_val; +} + long long check_program(long long program) { long long errs = 0; + long long en_vk = 0; + long long en_vo = 0; + long long en_vf = 0; + long long enums = 0; + long long ei = 0; + long long edef = 0; + long long ename = 0; + long long evs = 0; + long long evi = 0; + long long ev = 0; + long long fts = 0; + long long fields = 0; + long long fi = 0; + long long ok = 0; + long long en_funcs = 0; + long long efi = 0; + long long en_methods = 0; + long long emi = 0; long long funcs = 0; long long n = 0; long long idx = 0; @@ -10013,16 +9993,57 @@ long long check_program(long long program) { long long mdef = 0; long long okm = 0; long long e_len = 0; - long long ei = 0; long long ret_val = 0; ep_gc_push_root(&errs); + ep_gc_push_root(&en_vk); + ep_gc_push_root(&en_vo); + ep_gc_push_root(&en_vf); + ep_gc_push_root(&fts); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(errs); - errs = tmp_val; + errs = create_list(); + en_vk = create_list(); + en_vo = create_list(); + en_vf = create_list(); + if (length_list(program) > 5LL) { + enums = get_list(program, 5LL); + ei = 0LL; + while (ei < length_list(enums)) { + edef = get_list(enums, ei); + ename = get_list(edef, 1LL); + evs = get_list(edef, 2LL); + evi = 0LL; + while (evi < length_list(evs)) { + ev = get_list(evs, evi); + fts = create_list(); + fields = get_list(ev, 1LL); + fi = 0LL; + while (fi < length_list(fields)) { + ok = append_list(fts, get_list(get_list(fields, fi), 1LL)); + fi = (fi + 1LL); + } + ok = append_list(en_vk, get_list(ev, 0LL)); + ok = append_list(en_vo, ename); + ok = append_list(en_vf, fts); + evi = (evi + 1LL); + } + ei = (ei + 1LL); + } + } + en_funcs = get_list(program, 3LL); + efi = 0LL; + while (efi < length_list(en_funcs)) { + ok = en_check_stmts(get_list(get_list(en_funcs, efi), 3LL), errs, en_vk, en_vo, en_vf); + efi = (efi + 1LL); + } + if (length_list(program) > 6LL) { + en_methods = get_list(program, 6LL); + emi = 0LL; + while (emi < length_list(en_methods)) { + ok = en_check_stmts(get_list(get_list(en_methods, emi), 4LL), errs, en_vk, en_vo, en_vf); + emi = (emi + 1LL); + } } funcs = get_list(program, 3LL); n = length_list(funcs); @@ -10054,7 +10075,7 @@ long long check_program(long long program) { ret_val = 0LL; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(1); + ep_gc_pop_roots(5); return ret_val; } @@ -10636,11 +10657,7 @@ long long string_concat(long long s1, long long s2) { ep_gc_push_root(&res); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lst); - lst = tmp_val; - } + lst = create_list(); len1 = string_length((char*)s1); idx = 0LL; while (idx < len1) { @@ -10676,11 +10693,7 @@ long long cg_sanitize_name(long long name) { ret_val = (long long)"_main"; goto L_cleanup; } - { - long long tmp_val = create_list(); - free_list(kws); - kws = tmp_val; - } + kws = create_list(); ok = append_list(kws, (long long)"auto"); ok = append_list(kws, (long long)"break"); ok = append_list(kws, (long long)"case"); @@ -10802,17 +10815,9 @@ long long cg_int_to_str(long long n) { neg = 1LL; n = (0LL - n); } - { - long long tmp_val = create_list(); - free_list(lst); - lst = tmp_val; - } + lst = create_list(); temp = n; - { - long long tmp_val = create_list(); - free_list(digits); - digits = tmp_val; - } + digits = create_list(); while (temp > 0LL) { digit = (temp - ((temp / 10LL) * 10LL)); ok = append_list(digits, (digit + 48LL)); @@ -10849,11 +10854,7 @@ long long escape_string(long long s) { ep_gc_push_root(&res); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lst); - lst = tmp_val; - } + lst = create_list(); len = string_length((char*)s); idx = 0LL; while (idx < len) { @@ -10909,11 +10910,7 @@ long long join_strings(long long lines) { ep_gc_push_root(&res); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lst); - lst = tmp_val; - } + lst = create_list(); len = length_list(lines); idx = 0LL; while (idx < len) { @@ -10942,11 +10939,7 @@ long long create_codegen_state() { ep_gc_push_root(&state); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(state); - state = tmp_val; - } + state = create_list(); ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, (create_list() + 0LL)); ok = append_list(state, 0LL); @@ -12112,11 +12105,7 @@ long long var_returned_in_stmts(long long name, long long stmts) { stmt = get_list(stmts, idx); t = get_list(stmt, 0LL); if (t == 8LL) { - { - long long tmp_val = create_list(); - free_list(ids); - ids = tmp_val; - } + ids = create_list(); ok = collect_idents_expr(get_list(stmt, 1LL), ids); if (contains_string_val(ids, name) == 1LL) { ret_val = 1LL; @@ -12263,11 +12252,7 @@ long long gen_function(long long state, long long func) { if (is_async == 1LL) { cname = get_fn_c_name(func); aw_count = count_awaits_stmts(body); - { - long long tmp_val = create_list(); - free_list(async_locals); - async_locals = tmp_val; - } + async_locals = create_list(); al_i = 0LL; while (al_i < length_list(var_types_keys)) { okal = append_list(async_locals, get_list(var_types_keys, al_i)); @@ -12492,10 +12477,6 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon long long expr_str = 0; long long al = 0; long long ok = 0; - long long is_global = 0; - long long borrowed_keys = 0; - long long borrowed_values = 0; - long long is_borrowed = 0; long long line = 0; long long arl = 0; long long expr_type = 0; @@ -12593,33 +12574,12 @@ long long gen_statement(long long state, long long stmt, long long var_keys, lon goto L_cleanup; } } - is_global = is_global_var(name); - borrowed_keys = get_codegen_borrowed_keys(state); - borrowed_values = get_codegen_borrowed_values(state); - is_borrowed = map_get(borrowed_keys, borrowed_values, name); - if (((t == 4LL && is_global == 0LL) && is_borrowed == 0LL)) { - ok = emit(state, (long long)" {\n"); - line = (long long)" long long tmp_val = "; - line = string_concat(line, expr_str); - line = string_concat(line, (long long)";\n"); - ok = emit(state, line); - line = (long long)" free_list("; - line = string_concat(line, name); - line = string_concat(line, (long long)");\n"); - ok = emit(state, line); - line = (long long)" "; - line = string_concat(line, name); - line = string_concat(line, (long long)" = tmp_val;\n"); - ok = emit(state, line); - ok = emit(state, (long long)" }\n"); - } else { line = (long long)" "; line = string_concat(line, name); line = string_concat(line, (long long)" = "); line = string_concat(line, expr_str); line = string_concat(line, (long long)";\n"); ok = emit(state, line); - } ret_val = 0LL; goto L_cleanup; } @@ -13400,11 +13360,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon name = get_list(expr, 1LL); args = get_list(expr, 2LL); args_len = length_list(args); - { - long long tmp_val = create_list(); - free_list(formatted_args); - formatted_args = tmp_val; - } + formatted_args = create_list(); idx = 0LL; while (idx < args_len) { arg = get_list(args, idx); @@ -13442,11 +13398,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon ok = append_list(formatted_args, casted); idx = (idx + 1LL); } - { - long long tmp_val = create_list(); - free_list(args_str_list); - args_str_list = tmp_val; - } + args_str_list = create_list(); idx = 0LL; while (idx < args_len) { arg_item = get_list(formatted_args, idx); @@ -13692,19 +13644,11 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon cidx = get_list(state, 13LL); dummy = set_list(state, 13LL, (cidx + 1LL)); cname = string_concat((long long)"_ep_closure_", cg_int_to_str(cidx)); - { - long long tmp_val = create_list(); - free_list(raw_names); - raw_names = tmp_val; - } + raw_names = create_list(); okr = collect_idents_stmts(body, raw_names); func_keys = get_list(state, 3LL); p_len = length_list(params); - { - long long tmp_val = create_list(); - free_list(captured); - captured = tmp_val; - } + captured = create_list(); rn_len = length_list(raw_names); rn_i = 0LL; while (rn_i < rn_len) { @@ -13769,16 +13713,8 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon oku = emit(state, unp); cp_i = (cp_i + 1LL); } - { - long long tmp_val = create_list(); - free_list(c_keys); - c_keys = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(c_values); - c_values = tmp_val; - } + c_keys = create_list(); + c_values = create_list(); ov_len = length_list(var_keys); ov_i = 0LL; while (ov_i < ov_len) { @@ -13791,16 +13727,8 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon okpv = map_put(c_keys, c_values, get_list(p_node, 0LL), 1LL); pv_i = (pv_i + 1LL); } - { - long long tmp_val = create_list(); - free_list(b_keys); - b_keys = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(b_values); - b_values = tmp_val; - } + b_keys = create_list(); + b_values = create_list(); okbv = collect_var_types(state, body, b_keys, b_values); bv_len = length_list(b_keys); bv_i = 0LL; @@ -13912,11 +13840,7 @@ long long get_c_main_source() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"\n/* Bootstrapper C main */\n"); ok = append_list(lines, (long long)"void __ep_init_constants(void);\n"); ok = append_list(lines, (long long)"int main(int argc, char** argv) {\n"); @@ -13956,11 +13880,7 @@ long long get_c_test_main_source(long long program) { funcs = get_list(program, 3LL); funcs_len = length_list(funcs); - { - long long tmp_val = create_list(); - free_list(test_cases); - test_cases = tmp_val; - } + test_cases = create_list(); idx = 0LL; while (idx < funcs_len) { func = get_list(funcs, idx); @@ -13975,11 +13895,7 @@ long long get_c_test_main_source(long long program) { idx = (idx + 1LL); } test_count = length_list(test_cases); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"\n/* Test runner C main */\n"); ok = append_list(lines, (long long)"#include \n"); ok = append_list(lines, (long long)"#include \n"); @@ -14104,11 +14020,7 @@ long long collect_all_spawns(long long program) { ep_gc_push_root(&spawn_list); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(spawn_list); - spawn_list = tmp_val; - } + spawn_list = create_list(); funcs = get_list(program, 3LL); len = length_list(funcs); idx = 0LL; @@ -14137,11 +14049,7 @@ long long clone_list(long long lst) { ep_gc_push_root(&new_lst); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(new_lst); - new_lst = tmp_val; - } + new_lst = create_list(); len = length_list(lst); idx = 0LL; while (idx < len) { @@ -14642,71 +14550,23 @@ long long check_safety_stmts(long long func, long long stmts, long long var_keys ret_val = 0LL; goto L_cleanup; } - { - long long tmp_val = clone_list(state_keys); - free_list(then_state_keys); - then_state_keys = tmp_val; - } - { - long long tmp_val = clone_list(state_values); - free_list(then_state_values); - then_state_values = tmp_val; - } - { - long long tmp_val = clone_list(borrow_keys); - free_list(then_borrow_keys); - then_borrow_keys = tmp_val; - } - { - long long tmp_val = clone_list(borrow_values); - free_list(then_borrow_values); - then_borrow_values = tmp_val; - } - { - long long tmp_val = clone_list(count_keys); - free_list(then_count_keys); - then_count_keys = tmp_val; - } - { - long long tmp_val = clone_list(count_values); - free_list(then_count_values); - then_count_values = tmp_val; - } + then_state_keys = clone_list(state_keys); + then_state_values = clone_list(state_values); + then_borrow_keys = clone_list(borrow_keys); + then_borrow_values = clone_list(borrow_values); + then_count_keys = clone_list(count_keys); + then_count_values = clone_list(count_values); ok = check_safety_stmts(func, then_b, var_keys, var_values, then_state_keys, then_state_values, then_borrow_keys, then_borrow_values, then_count_keys, then_count_values); if (ok == 0LL) { ret_val = 0LL; goto L_cleanup; } - { - long long tmp_val = clone_list(state_keys); - free_list(else_state_keys); - else_state_keys = tmp_val; - } - { - long long tmp_val = clone_list(state_values); - free_list(else_state_values); - else_state_values = tmp_val; - } - { - long long tmp_val = clone_list(borrow_keys); - free_list(else_borrow_keys); - else_borrow_keys = tmp_val; - } - { - long long tmp_val = clone_list(borrow_values); - free_list(else_borrow_values); - else_borrow_values = tmp_val; - } - { - long long tmp_val = clone_list(count_keys); - free_list(else_count_keys); - else_count_keys = tmp_val; - } - { - long long tmp_val = clone_list(count_values); - free_list(else_count_values); - else_count_values = tmp_val; - } + else_state_keys = clone_list(state_keys); + else_state_values = clone_list(state_values); + else_borrow_keys = clone_list(borrow_keys); + else_borrow_values = clone_list(borrow_values); + else_count_keys = clone_list(count_keys); + else_count_values = clone_list(count_values); if (else_b != 0LL) { ok = check_safety_stmts(func, else_b, var_keys, var_values, else_state_keys, else_state_values, else_borrow_keys, else_borrow_values, else_count_keys, else_count_values); if (ok == 0LL) { @@ -14772,16 +14632,8 @@ long long check_safety_stmts(long long func, long long stmts, long long var_keys ret_val = 0LL; goto L_cleanup; } - { - long long tmp_val = clone_list(state_keys); - free_list(start_state_keys); - start_state_keys = tmp_val; - } - { - long long tmp_val = clone_list(state_values); - free_list(start_state_values); - start_state_values = tmp_val; - } + start_state_keys = clone_list(state_keys); + start_state_values = clone_list(state_values); ok = check_safety_stmts(func, body, var_keys, var_values, state_keys, state_values, borrow_keys, borrow_values, count_keys, count_values); if (ok == 0LL) { ret_val = 0LL; @@ -15061,11 +14913,7 @@ long long generate_c(long long program, long long is_test_mode) { ep_gc_push_root(&c_code); ep_gc_maybe_collect(); - { - long long tmp_val = create_codegen_state(); - free_list(state); - state = tmp_val; - } + state = create_codegen_state(); ok = analyze_return_types(state, program); if (length_list(program) > 8LL) { it_impls = get_list(program, 8LL); @@ -15086,11 +14934,7 @@ long long generate_c(long long program, long long is_test_mode) { } ok = emit(state, get_c_runtime_source()); prog_len = length_list(program); - { - long long tmp_val = create_list(); - free_list(field_slots); - field_slots = tmp_val; - } + field_slots = create_list(); if (prog_len > 4LL) { struct_defs = get_list(program, 4LL); sd_len = length_list(struct_defs); @@ -15128,11 +14972,7 @@ long long generate_c(long long program, long long is_test_mode) { slot_line = string_concat(slot_line, cg_int_to_str((length_list(field_slots) + 8LL))); slot_line = string_concat(slot_line, (long long)"\n"); ok = emit(state, slot_line); - { - long long tmp_val = create_list(); - free_list(tag_slots); - tag_slots = tmp_val; - } + tag_slots = create_list(); if (prog_len > 5LL) { enum_defs = get_list(program, 5LL); ed_len = length_list(enum_defs); @@ -15180,11 +15020,7 @@ long long generate_c(long long program, long long is_test_mode) { while (vpv_i < vpv_len) { vp_var = get_list(vp_variants, vpv_i); vp_fields = get_list(vp_var, 1LL); - { - long long tmp_val = create_list(); - free_list(vp_codes); - vp_codes = tmp_val; - } + vp_codes = create_list(); vpf_i = 0LL; vpf_len = length_list(vp_fields); while (vpf_i < vpf_len) { @@ -15271,16 +15107,8 @@ long long generate_c(long long program, long long is_test_mode) { ok = emit(state, (long long)"void __ep_init_constants(void) {\n"); ok = emit(state, (long long)" ep_gc_mark_globals_major = __ep_mark_globals_major;\n"); ok = emit(state, (long long)" ep_gc_mark_globals_minor = __ep_mark_globals_minor;\n"); - { - long long tmp_val = create_list(); - free_list(gk); - gk = tmp_val; - } - { - long long tmp_val = create_list(); - free_list(gv); - gv = tmp_val; - } + gk = create_list(); + gv = create_list(); ci = 0LL; while (ci < const_n) { cstmt = get_list(constants, ci); @@ -15360,11 +15188,7 @@ long long generate_c(long long program, long long is_test_mode) { idx = (idx + 1LL); } ok = emit(state, (long long)"\n"); - { - long long tmp_val = collect_all_spawns(program); - free_list(spawn_list); - spawn_list = tmp_val; - } + spawn_list = collect_all_spawns(program); dummy = set_codegen_spawn_list(state, spawn_list); spawn_len = length_list(spawn_list); ok = emit(state, (long long)"\n/* Thread Spawn Wrappers */\n"); @@ -15452,11 +15276,7 @@ long long generate_c(long long program, long long is_test_mode) { md_idx = (md_idx + 1LL); } } - { - long long tmp_val = create_list(); - free_list(emitted_methods); - emitted_methods = tmp_val; - } + emitted_methods = create_list(); if (prog_len > 8LL) { trait_impls = get_list(program, 8LL); ti_len = length_list(trait_impls); @@ -15528,11 +15348,7 @@ long long ep_rt_core_0() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"#include \n"); ok = append_list(lines, (long long)"#include \n"); ok = append_list(lines, (long long)"#include \n"); @@ -15698,11 +15514,7 @@ long long ep_rt_core_1() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"#elif defined(_WIN32)\n"); ok = append_list(lines, (long long)" #include \n"); ok = append_list(lines, (long long)" #include \n"); @@ -15868,11 +15680,7 @@ long long ep_rt_core_2() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" cur = cur->next;\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" timer->next = cur->next;\n"); @@ -16038,11 +15846,7 @@ long long ep_rt_core_3() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" if (res != -999999) {\n"); ok = append_list(lines, (long long)" if (task->fut) {\n"); ok = append_list(lines, (long long)" task->fut->value = res;\n"); @@ -16208,11 +16012,7 @@ long long ep_rt_core_4() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" }\n"); @@ -16378,11 +16178,7 @@ long long ep_rt_core_5() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" oneshot read-readiness task with the loop, return the future. When fd becomes\n"); ok = append_list(lines, (long long)" readable, ep_async_wait_step re-enqueues the task; its step completes the future\n"); ok = append_list(lines, (long long)" and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n"); @@ -16548,11 +16344,7 @@ long long ep_rt_core_6() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"static __thread void* volatile ep_thread_local_top = NULL;\n"); ok = append_list(lines, (long long)"static __thread void* ep_thread_local_bottom = NULL;\n"); ok = append_list(lines, (long long)"\n"); @@ -16718,11 +16510,7 @@ long long ep_rt_core_7() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" active check, it will see sp=0 and walk no roots instead\n"); ok = append_list(lines, (long long)" of dereferencing stale __thread pointers */\n"); ok = append_list(lines, (long long)" if (ep_thread_gc_states[i]) {\n"); @@ -16888,11 +16676,7 @@ long long ep_rt_core_8() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n"); ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n"); ok = append_list(lines, (long long)" return obj;\n"); @@ -17058,11 +16842,7 @@ long long ep_rt_core_9() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n"); ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n"); ok = append_list(lines, (long long)" if (!ep_thread_tops[t]) continue;\n"); @@ -17228,11 +17008,7 @@ long long ep_rt_core_10() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" /* Mark active tasks in the scheduler run queue for minor collection */\n"); ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n"); @@ -17398,11 +17174,7 @@ long long ep_rt_core_11() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"static void ep_gc_maybe_collect(void) {\n"); ok = append_list(lines, (long long)" if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n"); ok = append_list(lines, (long long)" /* Safepoint: lock-free fast check, then park under the lock if a collection\n"); @@ -17568,11 +17340,7 @@ long long ep_rt_core_12() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); ok = append_list(lines, (long long)" if (ep_channel_count < EP_MAX_CHANNELS) {\n"); ok = append_list(lines, (long long)" ep_channel_registry[ep_channel_count++] = chan;\n"); @@ -17738,11 +17506,7 @@ long long ep_rt_core_13() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" if (chan) {\n"); ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); ok = append_list(lines, (long long)" if (chan->size > 0) {\n"); @@ -17908,11 +17672,7 @@ long long ep_rt_core_14() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n"); ok = append_list(lines, (long long)" serv_addr.sin_addr.s_addr = INADDR_ANY;\n"); ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n"); @@ -18078,11 +17838,7 @@ long long ep_rt_core_15() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"long long ep_dlcall1(long long fptr, long long a0) {\n"); ok = append_list(lines, (long long)" return ((ep_fn1)fptr)(a0);\n"); ok = append_list(lines, (long long)"}\n"); @@ -18248,11 +18004,7 @@ long long ep_rt_core_16() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)"}\n"); @@ -18418,11 +18170,7 @@ long long ep_rt_core_17() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" map->entries[next_h].value = 0;\n"); ok = append_list(lines, (long long)" map->entries[next_h].used = 0;\n"); ok = append_list(lines, (long long)" map->size--;\n"); @@ -18588,11 +18336,7 @@ long long ep_rt_core_18() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* Filesystem Operations */\n"); @@ -18758,11 +18502,7 @@ long long ep_rt_core_19() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" \"%s%s\"\n"); ok = append_list(lines, (long long)" \"\\r\\n\",\n"); ok = append_list(lines, (long long)" method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n"); @@ -18928,11 +18668,7 @@ long long ep_rt_core_20() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" result[64] = '\\0';\n"); @@ -19098,11 +18834,7 @@ long long ep_rt_core_21() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n"); ok = append_list(lines, (long long)" unsigned char padding[64];\n"); ok = append_list(lines, (long long)" memset(padding, 0, 64); padding[0] = 0x80;\n"); @@ -19268,11 +19000,7 @@ long long ep_rt_core_22() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* SQLite type-safe wrappers — marshal between int and long long */\n"); ok = append_list(lines, (long long)"#ifdef EP_HAS_SQLITE\n"); @@ -19438,11 +19166,7 @@ long long ep_rt_core_23() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); ok = append_list(lines, (long long)" return empty;\n"); ok = append_list(lines, (long long)" }\n"); @@ -19608,11 +19332,7 @@ long long ep_rt_core_24() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" fputs(content, f);\n"); ok = append_list(lines, (long long)" fclose(f);\n"); ok = append_list(lines, (long long)" return 1;\n"); @@ -19778,11 +19498,7 @@ long long ep_rt_core_25() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"long long ep_setenv(long long name_ptr, long long val_ptr) {\n"); ok = append_list(lines, (long long)" return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n"); ok = append_list(lines, (long long)"}\n"); @@ -19948,11 +19664,7 @@ long long ep_rt_core_26() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n"); ok = append_list(lines, (long long)" AcquireSRWLockShared((SRWLOCK*)rwl);\n"); @@ -20118,11 +19830,7 @@ long long ep_rt_core_27() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"typedef struct {\n"); ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n"); ok = append_list(lines, (long long)" pthread_cond_t cond;\n"); @@ -20288,11 +19996,7 @@ long long ep_rt_core_28() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" regfree(®ex);\n"); ok = append_list(lines, (long long)" return (long long)result;\n"); ok = append_list(lines, (long long)"}\n"); @@ -20458,11 +20162,7 @@ long long ep_rt_core_29() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" if (strncmp(p, old_str, old_len) == 0) {\n"); ok = append_list(lines, (long long)" memcpy(dst, new_str, new_len);\n"); ok = append_list(lines, (long long)" dst += new_len;\n"); @@ -20628,11 +20328,7 @@ long long ep_rt_core_30() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" if (max <= min) return min;\n"); ok = append_list(lines, (long long)" /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n"); ok = append_list(lines, (long long)" unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n"); @@ -20798,11 +20494,7 @@ long long ep_rt_core_31() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)" for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n"); ok = append_list(lines, (long long)" unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n"); ok = append_list(lines, (long long)" for (int i = 0; i < 80; i++) {\n"); @@ -20900,11 +20592,7 @@ long long ep_rt_builtins_0() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* Built-in: string concatenation */\n"); ok = append_list(lines, (long long)"long long concat(long long a, long long b) {\n"); @@ -21070,11 +20758,7 @@ long long ep_rt_builtins_1() { ep_gc_push_root(&lines); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(lines); - lines = tmp_val; - } + lines = create_list(); ok = append_list(lines, (long long)"long long int_to_float(long long val) {\n"); ok = append_list(lines, (long long)" double d = (double)val;\n"); ok = append_list(lines, (long long)" long long result; memcpy(&result, &d, sizeof(double));\n"); @@ -21101,11 +20785,7 @@ long long get_shared_runtime_source() { ep_gc_push_root(&parts); ep_gc_maybe_collect(); - { - long long tmp_val = create_list(); - free_list(parts); - parts = tmp_val; - } + parts = create_list(); ok = append_list(parts, ep_rt_core_0()); ok = append_list(parts, ep_rt_core_1()); ok = append_list(parts, ep_rt_core_2()); diff --git a/ep_check.ep b/ep_check.ep index f290e96..b8ec1a7 100644 --- a/ep_check.ep +++ b/ep_check.ep @@ -123,9 +123,159 @@ define check_function with func and errs: set okb to check_stmts(get_list(func and 3) and errs) return 0 +# ---- Enum-variant field-type checking ---- +# The coarse "type name" of an argument expression, or "" if not statically +# known. A variant construction has the type of its owning enum. +define en_arg_type with arg and vk and vo: + if arg == 0: + return "" + set t to get_list(arg and 0) + if t == 26: # ENUM_CREATE -> owning enum name + set vn to string_concat(get_list(arg and 1) and "") + set i to 0 + repeat while i < length_list(vk): + if vn equals get_list(vk and i): + return get_list(vo and i) + set i to i + 1 + return "" + if t == 1: + return "Int" + if t == 2: + return "Str" + if t == 42: + return "Float" + if t == 31: + return "Bool" + return "" + +# The declared field type name at position `ai` for `variant`, or "" if the +# variant is unknown or `ai` is out of range. Returns a freshly-built string and +# binds NO list-typed local: the shared `vf` sub-lists are read strictly inline +# (`get_list(vf and i)` is never stored), so the caller-side cleanup pass can +# never free a list that is aliased into `vf`. (A stored alias here caused a +# heap-use-after-free — the auto-cleanup freed the shared sub-list.) +define en_field_type_at with variant and ai and vk and vf: + set vn to string_concat(variant and "") + set i to 0 + repeat while i < length_list(vk): + if vn equals get_list(vk and i): + if ai < length_list(get_list(vf and i)): + return string_concat(get_list(get_list(vf and i) and ai) and "") + return "" + set i to i + 1 + return "" + +define en_check_expr with expr and errs and vk and vo and vf: + if expr == 0: + return 0 + set t to get_list(expr and 0) + if t == 26: # ENUM_CREATE + set variant to get_list(expr and 1) + set args to get_list(expr and 2) + set ai to 0 + repeat while ai < length_list(args): + set arg to get_list(args and ai) + set ft to string_concat(en_field_type_at(variant and ai and vk and vf) and "") + if string_length(ft) > 0: + set at to string_concat(en_arg_type(arg and vk and vo) and "") + if string_length(at) > 0: + if at equals ft: + set noop to 0 + else: + set msg to string_concat("enum variant field type mismatch: expected " and ft) + set msg to string_concat(msg and " but got ") + set msg to string_concat(msg and at) + set ok to append_list(errs and msg) + set oka to en_check_expr(arg and errs and vk and vo and vf) + set ai to ai + 1 + return 0 + if t == 4 || t == 5 || t == 14: + set okl to en_check_expr(get_list(expr and 1) and errs and vk and vo and vf) + set okr to en_check_expr(get_list(expr and 3) and errs and vk and vo and vf) + return 0 + if t == 6: # CALL + set cargs to get_list(expr and 2) + set ci to 0 + repeat while ci < length_list(cargs): + set okc to en_check_expr(get_list(cargs and ci) and errs and vk and vo and vf) + set ci to ci + 1 + return 0 + if t == 18 || t == 20 || t == 21 || t == 32 || t == 33: + return en_check_expr(get_list(expr and 1) and errs and vk and vo and vf) + return 0 + +define en_check_stmts with stmts and errs and vk and vo and vf: + set i to 0 + repeat while i < length_list(stmts): + set stmt to get_list(stmts and i) + set t to get_list(stmt and 0) + if t == 7: + set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf) + if t == 8 || t == 9 || t == 36: + set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf) + if t == 10: + set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf) + set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf) + set eb to get_list(stmt and 3) + if eb != 0: + set ok to en_check_stmts(eb and errs and vk and vo and vf) + if t == 11: + set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf) + set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf) + if t == 28: + set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf) + set ok to en_check_stmts(get_list(stmt and 3) and errs and vk and vo and vf) + if t == 27: + set arms to get_list(stmt and 2) + set ari to 0 + repeat while ari < length_list(arms): + set ok to en_check_stmts(get_list(get_list(arms and ari) and 2) and errs and vk and vo and vf) + set ari to ari + 1 + set i to i + 1 + return 0 + # Entry point: returns 1 if OK, 0 (after printing) if the program is rejected. define check_program with program: set errs to create_list() + + # Build variant -> (owner enum, field type names) from the enum definitions, + # then check every enum construction's argument types against them. + set en_vk to create_list() + set en_vo to create_list() + set en_vf to create_list() + if length_list(program) > 5: + set enums to get_list(program and 5) + set ei to 0 + repeat while ei < length_list(enums): + set edef to get_list(enums and ei) + set ename to get_list(edef and 1) + set evs to get_list(edef and 2) + set evi to 0 + repeat while evi < length_list(evs): + set ev to get_list(evs and evi) + set fts to create_list() + set fields to get_list(ev and 1) + set fi to 0 + repeat while fi < length_list(fields): + set ok to append_list(fts and get_list(get_list(fields and fi) and 1)) + set fi to fi + 1 + set ok to append_list(en_vk and get_list(ev and 0)) + set ok to append_list(en_vo and ename) + set ok to append_list(en_vf and fts) + set evi to evi + 1 + set ei to ei + 1 + set en_funcs to get_list(program and 3) + set efi to 0 + repeat while efi < length_list(en_funcs): + set ok to en_check_stmts(get_list(get_list(en_funcs and efi) and 3) and errs and en_vk and en_vo and en_vf) + set efi to efi + 1 + if length_list(program) > 6: + set en_methods to get_list(program and 6) + set emi to 0 + repeat while emi < length_list(en_methods): + set ok to en_check_stmts(get_list(get_list(en_methods and emi) and 4) and errs and en_vk and en_vo and en_vf) + set emi to emi + 1 + set funcs to get_list(program and 3) set n to length_list(funcs) set idx to 0 diff --git a/ep_codegen.ep b/ep_codegen.ep index 91fc4dc..5c2ad18 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -1318,37 +1318,19 @@ define gen_statement with state and stmt and var_keys and var_values: set ok to emit(state and al) return 0 - set is_global to is_global_var(name) - set borrowed_keys to get_codegen_borrowed_keys(state) - set borrowed_values to get_codegen_borrowed_values(state) - set is_borrowed to map_get(borrowed_keys and borrowed_values and name) - if t == 4 && is_global == 0 && is_borrowed == 0: # TYPE_LIST - set ok to emit(state and " {\n") - - set line to " long long tmp_val = " - set line to string_concat(line and expr_str) - set line to string_concat(line and ";\n") - set ok to emit(state and line) - - set line to " free_list(" - set line to string_concat(line and name) - set line to string_concat(line and ");\n") - set ok to emit(state and line) - - set line to " " - set line to string_concat(line and name) - set line to string_concat(line and " = tmp_val;\n") - set ok to emit(state and line) - - set ok to emit(state and " }\n") - else: - # Normal assignment - set line to " " - set line to string_concat(line and name) - set line to string_concat(line and " = ") - set line to string_concat(line and expr_str) - set line to string_concat(line and ";\n") - set ok to emit(state and line) + # Reassigning a list local does NOT free the previous value. Auto-freeing + # the old list here was unsound in the same way cleanup-free was (see the + # note in gen_function): it freed lists that had escaped via a container — + # e.g. a per-iteration `fts` appended into an outer list — corrupting that + # shared value on the next iteration (a heap-use-after-free). The + # generational GC reclaims genuinely-unreachable lists after their roots + # go out of scope, which is correct without ownership tracking. + set line to " " + set line to string_concat(line and name) + set line to string_concat(line and " = ") + set line to string_concat(line and expr_str) + set line to string_concat(line and ";\n") + set ok to emit(state and line) return 0 if type == 8: # NODE_RETURN From 130cd6eb8f32168ac9ab793854673d8863986021 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 02:42:35 +0100 Subject: [PATCH 45/51] feat(transpiler,async): JS trait-impl/enum-class emission + async_wait_readable builtin - emit_js.rs: enums now transpile to ES classes carrying a variant tag and an ordered payload-field list, and `implement Trait for Type` methods are emitted as prototype methods, so `value.method(...)` dispatches to the right impl and positional pattern-binding works in the JS backend. - async_wait_readable(fd) -> Future: registered in the type checker and C codegen and implemented in the runtime; suspends an async task until the fd is readable so I/O-bound tasks yield the event-loop thread instead of blocking. run_tests.sh 69/69, zero cargo warnings. Co-Authored-By: Claude Fable 5 --- src/emit_js.rs | 86 +++++++++++++++++++++++++++++++---------------- src/type_check.rs | 4 +++ 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/emit_js.rs b/src/emit_js.rs index eb291b2..02a4200 100644 --- a/src/emit_js.rs +++ b/src/emit_js.rs @@ -53,12 +53,22 @@ pub fn emit_js_from_ep(program: &Program) -> String { out.push('\n'); } - // Method definitions → standalone functions or class methods + // Delegate methods (`define m on Type`) → prototype methods on the class. for md in &program.method_defs { emit_method_def(&mut out, md); out.push('\n'); } + // Trait implementations (`implement Trait for Type`) → prototype methods, + // so `value.method(...)` dispatches to the right impl at runtime. + for ti in &program.trait_impls { + for m in &ti.methods { + let params: Vec = m.params.iter().map(|(n, _, _)| n.clone()).collect(); + emit_proto_method(&mut out, &ti.for_type, &m.name, ¶ms, &m.body, m.is_async); + } + if !ti.methods.is_empty() { out.push('\n'); } + } + // Functions for func in &program.functions { emit_function(&mut out, func); @@ -112,44 +122,57 @@ fn type_default_js(ta: &TypeAnnotation) -> &'static str { } fn emit_enum_def(out: &mut String, ed: &EnumDef) { - out.push_str(&format!("const {} = Object.freeze({{\n", ed.name)); + // A choice/enum becomes a class. Each value carries its variant tag and an + // ordered list of payload fields, so pattern matching can bind positionally + // and so delegate methods (`define m on Enum`) can hang off the prototype. + out.push_str(&format!("class {} {{\n", ed.name)); + out.push_str(" constructor(_variant, _fields) { this._variant = _variant; this._fields = _fields || []; }\n"); for (vname, fields) in &ed.variants { if fields.is_empty() { - out.push_str(&format!(" {}: \"{}\",\n", vname, vname)); + // Fieldless variant: a stable singleton so equality comparisons work. + out.push_str(&format!(" static {} = new {}(\"{}\", []);\n", vname, ed.name, vname)); } else { let params: Vec = fields.iter().map(|(n, _)| n.clone()).collect(); out.push_str(&format!( - " {}: ({}) => ({{ _variant: \"{}\", {} }}),\n", - vname, - params.join(", "), - vname, - params.iter().map(|p| format!("{p}")).collect::>().join(", ") + " static {}({}) {{ return new {}(\"{}\", [{}]); }}\n", + vname, params.join(", "), ed.name, vname, params.join(", ") )); } } - out.push_str("});\n"); + out.push_str("}\n"); + // Top-level aliases so `PlayerType(x)` and fieldless `Foo` work unqualified. + for (vname, _fields) in &ed.variants { + out.push_str(&format!("const {} = {}.{};\n", vname, ed.name, vname)); + } } fn emit_method_def(out: &mut String, md: &MethodDef) { - let func_name = format!("{}_{}", md.struct_name.to_lowercase(), md.name); - let mut params = vec!["self".to_string()]; - let param_names: HashSet = md.params.iter().map(|(n, _, _)| n.clone()).collect(); - for (name, _, _) in &md.params { - params.push(name.clone()); - } - out.push_str(&format!("function {}({}) {{\n", func_name, params.join(", "))); - // Pre-declare all local variables + let params: Vec = md.params.iter().map(|(n, _, _)| n.clone()).collect(); + emit_proto_method(out, &md.struct_name, &md.name, ¶ms, &md.body, false); +} + +/// Emit a method as `ClassName.prototype.name = function(params) { ... }`, with +/// `self` bound to the receiver. Works for both struct impls and enum delegates, +/// since structs and enums alike are emitted as JS classes. +fn emit_proto_method(out: &mut String, class_name: &str, name: &str, + params: &[String], body: &[Stmt], is_async: bool) { + let kw = if is_async { "async function" } else { "function" }; + out.push_str(&format!("{}.prototype.{} = {}({}) {{\n", class_name, name, kw, params.join(", "))); + indent(out, 1); + out.push_str("const self = this;\n"); + // Pre-declare locals (excluding params and `self`). + let param_names: HashSet = params.iter().cloned().collect(); let mut local_vars = HashSet::new(); - collect_set_names_js(&md.body, &mut local_vars); + collect_set_names_js(body, &mut local_vars); let locals: Vec<&String> = local_vars.iter().filter(|n| !param_names.contains(*n) && *n != "self").collect(); if !locals.is_empty() { indent(out, 1); out.push_str(&format!("let {};\n", locals.iter().map(|n| n.as_str()).collect::>().join(", "))); } - for stmt in &md.body { + for stmt in body { emit_stmt(out, &stmt.node, 1); } - out.push_str("}\n"); + out.push_str("};\n"); } fn emit_function(out: &mut String, func: &Function) { @@ -283,23 +306,24 @@ fn emit_stmt(out: &mut String, stmt: &StmtNode, depth: usize) { indent(out, depth); out.push_str("switch ("); emit_expr(out, &expr.node); - out.push_str(") {\n"); + out.push_str("._variant) {\n"); for (variant, bindings, body) in arms { indent(out, depth + 1); - out.push_str(&format!("case \"{}\":\n", variant)); - if !bindings.is_empty() { - for (i, b) in bindings.iter().enumerate() { - indent(out, depth + 2); - out.push_str(&format!("const {} = ", b)); - emit_expr(out, &expr.node); - out.push_str(&format!(".data[{}];\n", i)); - } + // Braced case so per-arm `const` bindings don't collide. + out.push_str(&format!("case \"{}\": {{\n", variant)); + for (i, b) in bindings.iter().enumerate() { + indent(out, depth + 2); + out.push_str(&format!("const {} = ", b)); + emit_expr(out, &expr.node); + out.push_str(&format!("._fields[{}];\n", i)); } for s in body { emit_stmt(out, &s.node, depth + 2); } indent(out, depth + 2); out.push_str("break;\n"); + indent(out, depth + 1); + out.push_str("}\n"); } indent(out, depth); out.push_str("}\n"); @@ -469,6 +493,10 @@ fn emit_expr(out: &mut String, expr: &ExprNode) { } "push" } + "create_list" if args.is_empty() => { + out.push_str("[]"); + return; + } "get_list" => { if args.len() >= 2 { emit_expr(out, &args[0].node); diff --git a/src/type_check.rs b/src/type_check.rs index f752b3d..11c8fea 100644 --- a/src/type_check.rs +++ b/src/type_check.rs @@ -973,6 +973,10 @@ impl TypeChecker { let v_cancel_fut = self.fresh_var(); self.func_types.insert("cancel_task".into(), (vec![v_cancel_fut], MonoType::Unit)); self.func_types.insert("sleep_ms".into(), (vec![MonoType::Int], MonoType::Future(Box::new(MonoType::Int)))); + // async_wait_readable(fd) -> Future: suspends until the socket fd is + // readable, so I/O-bound async tasks (e.g. agents awaiting an LLM response) + // yield the single event-loop thread to each other instead of blocking. + self.func_types.insert("async_wait_readable".into(), (vec![MonoType::Int], MonoType::Future(Box::new(MonoType::Int)))); // File system self.func_types.insert("read_file_content".into(), (vec![MonoType::Str], MonoType::DynStr)); From 91ff82a6d2fa34941620ef350e8215c317d1dbbb Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 08:36:02 +0100 Subject: [PATCH 46/51] =?UTF-8?q?docs:=20truth-up=20README=20+=20AGENT.md?= =?UTF-8?q?=20=E2=80=94=20accurate=20counts,=20zero-caveat=20self-hosting?= =?UTF-8?q?=20story?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: lead with the self-hosting/bootstrap achievement; real per-module line counts (reference ~30,094 lines / 24 modules; self-hosted ~6,400 lines / 6 modules; shared runtime 4,893 lines); a "what done means" gate table (69/69 Rust, 54/54 runnable, 9/9 rejections, byte-identical fixpoint, clang-only bootstrap); a Test Matrix section; badges updated to the real numbers. - AGENT.md: list all six self-hosted modules (ep_check + ep_optimizer were missing), correct the line count (5,800+ -> ~6,400), add the current gate results and the clang-only verify.sh gate. No code change; documentation only. All gates remain green. Co-Authored-By: Claude Fable 5 --- AGENT.md | 9 ++-- README.md | 158 +++++++++++++++++++++++++++++++++--------------------- 2 files changed, 103 insertions(+), 64 deletions(-) diff --git a/AGENT.md b/AGENT.md index b757112..10af701 100644 --- a/AGENT.md +++ b/AGENT.md @@ -60,9 +60,12 @@ cargo run -- epc.ep && ./epc tests/test_basic_math.ep && ./test_basic_math # Stronger gates (run after any epc-visible change): bash tests/run_fixpoint.sh # 3-stage byte-identical self-compile bash tests/run_epc_parity.sh # self-hosted coverage scoreboard (must not regress) +bash bootstrap/verify.sh # clang-only, Rust-free 3-stage fixpoint + parity + freshness ``` -The self-hosted compiler is 5,800+ lines of real ErnosPlain that exercises the full type system, all builtin functions, list operations, string operations, struct creation, pattern matching, and closures. If it doesn't compile, you broke something. +Current state of these gates: `run_tests.sh` **69/69**, `run_epc_parity.sh` **54/54 runnable + 9/9 compile-error rejections** (0 wrongly accepted), `run_fixpoint.sh` byte-identical, `bootstrap/verify.sh` green. + +The self-hosted compiler is ~6,400 lines of real ErnosPlain — `ep_lexer.ep` (lexer), `ep_parser.ep` (parser), `ep_check.ep` (semantic checker), `ep_optimizer.ep` (constant folding + DCE), `ep_codegen.ep` (C codegen), `epc.ep` (driver) — that exercises the full type system, all builtin functions, list/string operations, struct creation, pattern matching, closures, floats, traits, `try`/Result, and coroutine async. If it doesn't compile, you broke something. **Shared runtime.** `runtime/ep_runtime.c` + `runtime/ep_builtins.c` are the single source of truth for the emitted C runtime. The Rust compiler embeds them via `include_str!`; the self-hosted compiler embeds them via the generated `ep_runtime_gen.ep`. After editing either `runtime/*.c` file, regenerate: `./target/release/ernos tools/gen_runtime_ep.ep && ./tools/gen_runtime_ep`. @@ -152,7 +155,7 @@ Every language construct should read like a sentence a non-programmer could unde Symbol shortcuts (`+`, `<`, `==`, `&&`) are allowed as opt-in shorthands for experienced programmers. The plain English form is always the primary syntax. ### Self-Hosting is Non-Negotiable -The self-hosted compiler (`epc.ep` + modules) must always compile itself using the Rust bootstrap compiler. This is the ultimate integration test. If the type checker rejects the self-hosted compiler, the type checker is too strict — not the self-hosted compiler is wrong. The self-hosted compiler is 5,800+ lines of real, working ErnosPlain. It is the language's own dogfood. +The self-hosted compiler (`epc.ep` + modules) must always compile itself using the Rust bootstrap compiler. This is the ultimate integration test. If the type checker rejects the self-hosted compiler, the type checker is too strict — not the self-hosted compiler is wrong. The self-hosted compiler is ~6,400 lines of real, working ErnosPlain. It is the language's own dogfood, and it bootstraps from a frozen C snapshot with no Rust in the loop. ### Cross-Platform by Default Ernos must work on: @@ -516,7 +519,7 @@ Usage: `import "stdlib/bridge/sqlite"` — all functions use `ep_dlopen`/`ep_dls - Source files: `snake_case.ep` - Test files: `tests/test_feature_name.ep` with optional `tests/test_feature_name.expected` - Stdlib modules: `stdlib/module_name.ep` -- Self-hosted compiler: `epc.ep`, `ep_lexer.ep`, `ep_parser.ep`, `ep_codegen.ep` +- Self-hosted compiler: `epc.ep`, `ep_lexer.ep`, `ep_parser.ep`, `ep_check.ep`, `ep_optimizer.ep`, `ep_codegen.ep` (+ generated `ep_runtime_gen.ep`) - Generated C: `filename_compiled.c` (temporary, cleaned up by compiler) - Generated binary: `./dirname/filename` (next to source file, same stem) diff --git a/README.md b/README.md index 4f24c47..7dbcc7b 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@

Ernos Programming Language

-

A compiled language with plain English syntax, unification-based type inference, garbage-collected memory with ownership safety checks, and C-level performance.

+

A compiled language with plain-English syntax, unification-based type inference, garbage-collected memory with ownership-safety checks, C-level performance — and a compiler that compiles itself all the way down to a Rust-free, clang-only bootstrap.

Version - Tests - Performance - Platform - Self-Hosted + Rust suite 69/69 + Self-hosted parity 54/54 + Rejection gate 9/9 + Clang-only bootstrap + Byte-identical fixpoint + Platform

--- @@ -31,6 +33,17 @@ define main: **No curly braces. No semicolons. No noise.** Just code that reads like instructions. +### The headline: Ernos compiles itself, with no Rust in the loop + +Ernos ships **two** complete compilers for the same language: + +- **`ernos`** — the reference compiler, written in Rust (~30k lines). +- **`epc`** — the self-hosted compiler, **written entirely in Ernos** (`ep_lexer.ep`, `ep_parser.ep`, `ep_check.ep`, `ep_optimizer.ep`, `ep_codegen.ep`, `epc.ep`). + +`epc` compiles **every one of the 54 runnable test programs**, rejects **all 9** compile-error tests through its own semantic checker, and — compiling its own source — reaches a **byte-identical fixpoint** (`gen2 == gen3`). A frozen C snapshot (`bootstrap/epc_bootstrap.c`) means the whole toolchain rebuilds from **clang alone** — no Rust, no `cargo`, no bootstrap chicken-and-egg. This is verified end-to-end on every change, **with zero disclosed caveats**. + +> `clang bootstrap/epc_bootstrap.c -o epc && ./epc epc.ep` → a working compiler that recompiles itself and passes the full suite. + --- ## Why Ernos? @@ -294,86 +307,109 @@ Source (.ep) > **Note:** The codegen phase performs additional ownership checks (use-after-move, borrow violations) as a safety net alongside the dedicated borrow checker. Both must pass for compilation to succeed. -### Compiler Modules +### Reference compiler (Rust) — `~30,000` lines across 24 modules | File | Lines | Description | |------|-------|-------------| -| `src/lexer.rs` | ~900 | Tokenizer with indentation tracking | -| `src/parser.rs` | ~1,640 | Recursive descent parser with Pratt precedence | -| `src/type_check.rs` | ~1,900 | Type inference via unification (HM-style; no let-generalization) | -| `src/borrow_check.rs` | ~830 | Ownership, borrowing, Send/Sync analysis | -| `src/optimizer.rs` | ~1,450 | Constant folding, DCE, CSE, LICM, inlining, loop unrolling | -| `src/codegen.rs` | ~7,700 | C code generation with full runtime | -| `src/llvm_codegen.rs` | ~80 | LLVM IR backend (via clang -emit-llvm) | -| `src/lsp.rs` | ~1,200 | Language Server Protocol implementation | -| `src/diagnostics.rs` | ~380 | Rich error reporting with ANSI colors | -| `src/native_codegen.rs` | ~660 | ARM64 native assembly backend (macOS + Linux) | -| `src/x86_64_codegen.rs` | ~620 | x86_64 native assembly backend (macOS + Linux) | -| `src/bind_c.rs` | ~1,440 | C header binding generator (zero-dependency) | -| `src/main.rs` | ~2,090 | CLI, imports, REPL, compilation pipeline | -| `src/transpile_py.rs` | ~1,880 | Python → ErnosPlain transpiler | -| `src/transpile_c.rs` | ~1,380 | C → ErnosPlain transpiler | -| `src/transpile_js.rs` | ~1,240 | JavaScript → ErnosPlain transpiler | -| `src/transpile_go.rs` | ~1,360 | Go → ErnosPlain transpiler | -| `src/transpile_rs.rs` | ~1,210 | Rust → ErnosPlain transpiler | -| `src/transpile_rb.rs` | ~1,080 | Ruby → ErnosPlain transpiler | -| `src/transpile_java.rs` | ~730 | Java → ErnosPlain transpiler | -| `src/transpile_ts.rs` | ~730 | TypeScript → ErnosPlain transpiler | -| `src/emit_c.rs` | ~570 | ErnosPlain → C emitter | -| `src/emit_js.rs` | ~590 | ErnosPlain → JavaScript emitter | -| `src/emit_python.rs` | ~640 | ErnosPlain → Python emitter | -| **Total** | **~32,750** | | +| `src/lexer.rs` | 896 | Tokenizer with indentation tracking | +| `src/parser.rs` | 1,639 | Recursive-descent parser with Pratt precedence | +| `src/type_check.rs` | 1,987 | Type inference via unification (HM-style; no let-generalization) | +| `src/borrow_check.rs` | 783 | Ownership, borrowing, Send/Sync analysis | +| `src/optimizer.rs` | 1,577 | Constant folding, DCE, CSE, LICM, inlining, loop unrolling | +| `src/codegen.rs` | 3,958 | C code generation (runtime lives in `runtime/`, embedded via `include_str!`) | +| `src/llvm_codegen.rs` | 76 | LLVM IR backend (via `clang -emit-llvm`) | +| `src/lsp.rs` | 1,198 | Language Server Protocol implementation | +| `src/diagnostics.rs` | 382 | Rich error reporting with ANSI colors | +| `src/native_codegen.rs` | 656 | ARM64 native-assembly backend (macOS + Linux) | +| `src/x86_64_codegen.rs` | 623 | x86-64 native-assembly backend (macOS + Linux) | +| `src/bind_c.rs` | 1,441 | C-header binding generator (zero-dependency) | +| `src/main.rs` | 2,087 | CLI, imports, REPL, compilation pipeline | +| `src/transpile_py.rs` | 2,673 | Python → Ernos transpiler | +| `src/transpile_c.rs` | 1,376 | C → Ernos transpiler | +| `src/transpile_js.rs` | 1,235 | JavaScript → Ernos transpiler | +| `src/transpile_go.rs` | 1,362 | Go → Ernos transpiler | +| `src/transpile_rs.rs` | 1,211 | Rust → Ernos transpiler | +| `src/transpile_rb.rs` | 1,080 | Ruby → Ernos transpiler | +| `src/transpile_java.rs` | 731 | Java → Ernos transpiler | +| `src/transpile_ts.rs` | 725 | TypeScript → Ernos transpiler | +| `src/emit_c.rs` | 569 | Ernos → C emitter | +| `src/emit_js.rs` | 622 | Ernos → JavaScript emitter (enums → ES classes, trait-impl dispatch) | +| `src/emit_python.rs` | 640 | Ernos → Python emitter | +| **Total** | **~30,094** | | + +### Shared C runtime — one source of truth, embedded by both compilers + +| File | Lines | Description | +|------|-------|-------------| +| `runtime/ep_runtime.c` | 4,732 | Generational GC (precise STW + conservative stack scan, write barrier, OOM-guarded allocators), pointer-safe object accessors, coroutine/`EpFuture` scheduler, TCP/HTTP, SQLite, crypto, FFI | +| `runtime/ep_builtins.c` | 161 | Builtin registration glue | + +The reference compiler embeds this via `include_str!`; the self-hosted compiler embeds the **byte-for-byte same source** through the generated `ep_runtime_gen.ep` (regenerate with `tools/gen_runtime_ep.ep`). Both compilers therefore emit the identical GC, accessors, and allocators — there is no "runtime drift" between them. --- -## Self-Hosting +## Self-Hosting & the Bootstrap + +This is the part most languages never finish. Ernos does — and proves it on every commit. -Ernos compiles its own compiler. The self-hosted compiler modules: +### The self-hosted compiler (`epc`) — written in Ernos, ~6,400 lines -- `ep_lexer.ep` — Lexer (incl. f-string desugaring, English keyword aliases) -- `ep_parser.ep` — Parser (incl. `import "x" as alias`) -- `ep_codegen.ep` — Code generator (closures, enums, the shared runtime) -- `epc.ep` — Compiler driver (module flattening, aliased imports) +| File | Lines | Description | +|------|-------|-------------| +| `ep_lexer.ep` | 817 | Lexer — indentation, f-string desugaring, English keyword aliases | +| `ep_parser.ep` | 1,449 | Parser — full grammar, `import "x" as alias`, traits, enums, closures | +| `ep_check.ep` | 301 | Semantic checker — reserved-name shadowing, Send-safety, list homogeneity, **enum-variant field-type checking** | +| `ep_optimizer.ep` | 122 | Constant folding + dead-code elimination | +| `ep_codegen.ep` | 3,342 | C code generator — closures, floats, traits, iterator protocol, `try`/Result, coroutine async, globals | +| `epc.ep` | 370 | Compiler driver — module flattening, aliased imports, `check`/`format`/`repl`/`doc` subcommands | +| `ep_runtime_gen.ep` | 5,070 | Generated: emits the shared C runtime verbatim | -The self-hosted compiler and the Rust compiler **share one C runtime** -(`runtime/ep_runtime.c` + `runtime/ep_builtins.c`), embedded by the Rust -compiler via `include_str!` and by the self-hosted compiler via the generated -`ep_runtime_gen.ep` (regenerate with `tools/gen_runtime_ep.ep`). So both emit -the same generational GC, pointer-safe accessors, and OOM-guarded allocators. +The self-hosted pipeline is a full `lex → parse → **check** → **optimize** → codegen`, not just a lex/parse/emit skeleton. -### Stable fixpoint +### What "done" actually means here -`epc` compiling itself reaches a **byte-identical fixpoint** (gen2 == gen3), -verified by `tests/run_fixpoint.sh`. Self-hosted coverage of the test suite is -tracked by `tests/run_epc_parity.sh` (currently 54/54 runnable programs; 8/9 -compile-error tests correctly rejected by the `ep_check.ep` semantic pass). -`epc check ` runs the checks without codegen. +| Gate | Result | Verified by | +|------|--------|-------------| +| Reference-compiler suite | **69 / 69** | `./run_tests.sh` | +| Self-hosted parity (runnable programs `epc` compiles + runs correctly) | **54 / 54** | `bash tests/run_epc_parity.sh` | +| Compile-error gate (programs `epc`'s checker must reject) | **9 / 9**, 0 wrongly accepted | `bash tests/run_epc_parity.sh` | +| 3-stage self-compilation fixpoint (`gen2 == gen3`, byte-identical) | **OK** | `bash tests/run_fixpoint.sh` | +| Rust-free, clang-only bootstrap → recompile → fixpoint → full suite | **OK** | `bash bootstrap/verify.sh` | +| Cargo build warnings | **0** | `cargo build --release` | -The self-hosted pipeline is `lex → parse → check → optimize → codegen` -(`ep_lexer` · `ep_parser` · `ep_check` · `ep_optimizer` · `ep_codegen`), covering -the full runnable test suite — closures, floats, traits + the iterator protocol, -`try`/Result, coroutine async, globals, and the English-alias surface. The Rust -compiler additionally provides the LSP and -cross-language transpilers. +The self-hosted checker is memory-safe under AddressSanitizer under **both** the Rust-built and the clang-built `epc` — the enum field-type pass, the last feature to land, was hardened against two distinct use-after-free classes (an aliased-sublist local and an unsound list-reassignment free) before it was accepted. -### Fully self-contained bootstrap (no Rust required) +### Fully self-contained bootstrap — no Rust required -`bootstrap/epc_bootstrap.c` is `epc` compiled by `epc` — the whole toolchain -builds with only a C compiler: +`bootstrap/epc_bootstrap.c` is `epc` compiled by `epc` (the fixpoint output). The whole toolchain rebuilds from a C compiler alone: ```bash bash bootstrap/build.sh # clang bootstrap/epc_bootstrap.c -> epc, then epc rebuilds epc.ep -bash bootstrap/verify.sh # proves the clang-only 3-stage fixpoint + artifact freshness +bash bootstrap/verify.sh # clang-only 3-stage fixpoint + parity suite + artifact-freshness check ``` -The Rust compiler builds the same self-hosted compiler and is still used for the -LSP and the cross-language transpilers: +`bootstrap/verify.sh` asserts the frozen C is **fresh** (matches the current `epc.ep`), so the artifact can never silently drift from source. Any change touching the self-hosted compiler must regenerate the bootstrap in the same commit. + +The Rust compiler builds the same self-hosted compiler and remains the home of the two features intentionally kept Rust-only — the **LSP** and the **cross-language transpilers**: ```bash ./target/release/ernos epc.ep # Rust compiler builds epc -./epc hello.ep && ./hello # epc compiles programs +./epc hello.ep && ./hello # epc compiles programs — no Rust involved +``` + +--- + +## The Test Matrix + +```bash +./run_tests.sh # reference (Rust) compiler: 69/69 +bash tests/run_epc_parity.sh # self-hosted: 54/54 runnable + 9/9 rejections +bash tests/run_fixpoint.sh # 3-stage byte-identical fixpoint +bash bootstrap/verify.sh # clang-only, Rust-free end-to-end proof ``` +Every one of the 63 programs in `tests/` (54 runnable + 9 compile-error) is exercised by **both** compilers. Conformance tests live in [`conformance/`](conformance/); the formal grammar and type/memory/concurrency rules are in [`spec/ernos-spec.md`](spec/ernos-spec.md). + --- ## Syntax Quick Reference From 69ae8834c17577327d349bc9d3dd441e0d6ae903 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 09:09:10 +0100 Subject: [PATCH 47/51] =?UTF-8?q?fix:=20differential-testing=20findings=20?= =?UTF-8?q?=E2=80=94=20miscompilation,=20grammar=20drift,=203=20type-safet?= =?UTF-8?q?y=20holes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adversarial differential testing (37 novel programs compiled by BOTH compilers, outputs diffed) surfaced and fixed: 1. MISCOMPILATION (self-hosted): `not (a > b)` emitted C `!a > b` — C precedence negated only `a`, silently flipping branches. Unary-not now parenthesizes its operand (ep_codegen.ep). 2. GRAMMAR DRIFT: the README-documented single-line closure `given x: return x * 2` parsed under epc but not under the Rust compiler. The Rust parser now accepts it (src/parser.rs). 3. CLOSURE NAME COLLISIONS (Rust): two functions using the same closure variable name, or one closure duplicated by loop unrolling, emitted colliding `_ep_closure_` C functions (clang redefinition error). Lifted closures now get globally unique names (src/codegen.rs). 4. TYPE-SAFETY HOLES (both compilers), all now hard errors in BOTH: - `returning Int` + `return "str"`: the README's own flagship rejection example was actually accepted (downgraded to a warning long ago). Literal return-type conflicts are hard errors again; the `return 0` null idiom and inference-var cases stay warnings. tests/test_return_type_mismatch.ep is now an expected_compile_error test (it previously codified the warning). - `"abc" + 5`: C pointer arithmetic in disguise (printed adjacent literal pool memory). Str operands in arithmetic now allowed ONLY as the `+ 0` int-cast idiom. - `double_it("five")` with `n as Int`: epc's checker now verifies call-site argument types against declared param types. The self-hosted parser now captures `as Type` annotations (param node index 2) instead of discarding them. New rejection tests: test_str_arithmetic, test_arg_type_mismatch. Gates: Rust suite 71/71; parity 53/53 runnable + 12/12 rejected (0 wrongly accepted); 3-stage fixpoint byte-identical; clang-only bootstrap verify green (re-frozen in this commit); ASAN clean on the checker paths; 37/37 adversarial programs agree across compilers; zero cargo warnings. Co-Authored-By: Claude Fable 5 --- bootstrap/epc_bootstrap.c | 257 ++++++++++++++++++++--- ep_check.ep | 146 +++++++++++-- ep_codegen.ep | 6 +- ep_parser.ep | 15 +- src/codegen.rs | 30 ++- src/parser.rs | 18 +- src/type_check.rs | 47 ++++- tests/test_arg_type_mismatch.ep | 9 + tests/test_return_type_mismatch.ep | 6 +- tests/test_return_type_mismatch.expected | 1 - tests/test_str_arithmetic.ep | 7 + 11 files changed, 465 insertions(+), 77 deletions(-) create mode 100644 tests/test_arg_type_mismatch.ep delete mode 100644 tests/test_return_type_mismatch.expected create mode 100644 tests/test_str_arithmetic.ep diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 322d7f2..8cce170 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -5000,8 +5000,9 @@ long long check_stmts(long long, long long); long long check_function(long long, long long); long long en_arg_type(long long, long long, long long); long long en_field_type_at(long long, long long, long long, long long); -long long en_check_expr(long long, long long, long long, long long, long long); -long long en_check_stmts(long long, long long, long long, long long, long long); +long long en_type_conflict(long long, long long, long long, long long); +long long en_check_expr(long long, long long, long long, long long, long long, long long, long long); +long long en_check_stmts(long long, long long, long long, long long, long long, long long, long long, long long); long long check_program(long long); long long opt_fold_expr(long long); long long opt_fold_stmts(long long); @@ -7999,13 +8000,17 @@ long long parse_param_list(long long state) { long long is_borrow = 0; long long tok_param = 0; long long param_name = 0; + long long ptype = 0; long long next_as = 0; + long long tok_ptype = 0; long long param_node = 0; long long ok = 0; long long loop = 0; long long tok_and = 0; long long next_tok3 = 0; long long is_borrow3 = 0; + long long ptype3 = 0; + long long tok_ptype3 = 0; long long ret_val = 0; ep_gc_maybe_collect(); @@ -8022,14 +8027,17 @@ long long parse_param_list(long long state) { } tok_param = advance_token(state); param_name = get_token_value(tok_param); + ptype = (long long)""; next_as = peek_token(state); if (get_token_type(next_as) == 42LL) { dummy = advance_token(state); - dummy = advance_token(state); + tok_ptype = advance_token(state); + ptype = get_token_value(tok_ptype); } param_node = (create_list() + 0LL); ok = append_list(param_node, param_name); ok = append_list(param_node, is_borrow); + ok = append_list(param_node, ptype); ok = append_list(params, param_node); loop = 1LL; while (loop == 1LL) { @@ -8044,14 +8052,17 @@ long long parse_param_list(long long state) { } tok_param = advance_token(state); param_name = get_token_value(tok_param); + ptype3 = (long long)""; next_as = peek_token(state); if (get_token_type(next_as) == 42LL) { dummy = advance_token(state); - dummy = advance_token(state); + tok_ptype3 = advance_token(state); + ptype3 = get_token_value(tok_ptype3); } param_node = (create_list() + 0LL); ok = append_list(param_node, param_name); ok = append_list(param_node, is_borrow3); + ok = append_list(param_node, ptype3); ok = append_list(params, param_node); } else { loop = 0LL; @@ -9827,7 +9838,69 @@ long long en_field_type_at(long long variant, long long ai, long long vk, long l return ret_val; } -long long en_check_expr(long long expr, long long errs, long long vk, long long vo, long long vf) { +long long en_type_conflict(long long dt, long long arg, long long vk, long long vo) { + long long dts = 0; + long long at = 0; + long long ret_val = 0; + + ep_gc_push_root(&dts); + ep_gc_push_root(&at); + ep_gc_maybe_collect(); + + dts = string_concat(dt, (long long)""); + if (string_length((char*)dts) == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + at = string_concat(en_arg_type(arg, vk, vo), (long long)""); + if (string_length((char*)at) == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + if ((strcmp((char*)dts, (char*)(long long)"Str") == 0)) { + if ((strcmp((char*)at, (char*)(long long)"Int") == 0)) { + if (get_list(arg, 0LL) == 1LL) { + if (get_list(arg, 1LL) == 0LL) { + ret_val = 0LL; + goto L_cleanup; + } + } + ret_val = 1LL; + goto L_cleanup; + } + if ((strcmp((char*)at, (char*)(long long)"Float") == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + if ((strcmp((char*)at, (char*)(long long)"Bool") == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + ret_val = 0LL; + goto L_cleanup; + } + if ((strcmp((char*)at, (char*)(long long)"Str") == 0)) { + if ((strcmp((char*)dts, (char*)(long long)"Int") == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + if ((strcmp((char*)dts, (char*)(long long)"Float") == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + if ((strcmp((char*)dts, (char*)(long long)"Bool") == 0)) { + ret_val = 1LL; + goto L_cleanup; + } + } + ret_val = 0LL; + goto L_cleanup; +L_cleanup: + ep_gc_pop_roots(2); + return ret_val; +} + +long long en_check_expr(long long expr, long long errs, long long vk, long long vo, long long vf, long long fk, long long fp) { long long t = 0; long long variant = 0; long long args = 0; @@ -9839,9 +9912,21 @@ long long en_check_expr(long long expr, long long errs, long long vk, long long long long msg = 0; long long ok = 0; long long oka = 0; + long long bl = 0; + long long bop = 0; + long long br = 0; + long long blt = 0; + long long brt = 0; + long long castok = 0; + long long castok2 = 0; long long okl = 0; long long okr = 0; + long long cname = 0; long long cargs = 0; + long long fi = 0; + long long pi = 0; + long long dt = 0; + long long amsg = 0; long long ci = 0; long long okc = 0; long long ret_val = 0; @@ -9849,6 +9934,11 @@ long long en_check_expr(long long expr, long long errs, long long vk, long long ep_gc_push_root(&ft); ep_gc_push_root(&at); ep_gc_push_root(&msg); + ep_gc_push_root(&blt); + ep_gc_push_root(&brt); + ep_gc_push_root(&cname); + ep_gc_push_root(&dt); + ep_gc_push_root(&amsg); ep_gc_maybe_collect(); if (expr == 0LL) { @@ -9876,49 +9966,112 @@ long long en_check_expr(long long expr, long long errs, long long vk, long long } } } - oka = en_check_expr(arg, errs, vk, vo, vf); + oka = en_check_expr(arg, errs, vk, vo, vf, fk, fp); ai = (ai + 1LL); } ret_val = 0LL; goto L_cleanup; } - if (((t == 4LL || t == 5LL) || t == 14LL)) { - okl = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf); - okr = en_check_expr(get_list(expr, 3LL), errs, vk, vo, vf); + if (t == 4LL) { + bl = get_list(expr, 1LL); + bop = get_list(expr, 2LL); + br = get_list(expr, 3LL); + blt = string_concat(en_arg_type(bl, vk, vo), (long long)""); + brt = string_concat(en_arg_type(br, vk, vo), (long long)""); + if ((strcmp((char*)blt, (char*)(long long)"Str") == 0)) { + castok = 0LL; + if (bop == 1LL) { + if (get_list(br, 0LL) == 1LL) { + if (get_list(br, 1LL) == 0LL) { + castok = 1LL; + } + } + } + if (castok == 0LL) { + ok = append_list(errs, (long long)"arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom"); + } + } + if ((strcmp((char*)brt, (char*)(long long)"Str") == 0)) { + castok2 = 0LL; + if (bop == 1LL) { + if (get_list(bl, 0LL) == 1LL) { + if (get_list(bl, 1LL) == 0LL) { + castok2 = 1LL; + } + } + } + if (castok2 == 0LL) { + ok = append_list(errs, (long long)"arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom"); + } + } + okl = en_check_expr(bl, errs, vk, vo, vf, fk, fp); + okr = en_check_expr(br, errs, vk, vo, vf, fk, fp); + ret_val = 0LL; + goto L_cleanup; + } + if ((t == 5LL || t == 14LL)) { + okl = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf, fk, fp); + okr = en_check_expr(get_list(expr, 3LL), errs, vk, vo, vf, fk, fp); ret_val = 0LL; goto L_cleanup; } if (t == 6LL) { + cname = string_concat(get_list(expr, 1LL), (long long)""); cargs = get_list(expr, 2LL); + fi = 0LL; + while (fi < length_list(fk)) { + if ((strcmp((char*)cname, (char*)get_list(fk, fi)) == 0)) { + pi = 0LL; + while (pi < length_list(cargs)) { + if (pi < length_list(get_list(fp, fi))) { + dt = string_concat(get_list(get_list(fp, fi), pi), (long long)""); + if (en_type_conflict(dt, get_list(cargs, pi), vk, vo) == 1LL) { + amsg = string_concat((long long)"argument type mismatch in call to '", cname); + amsg = string_concat(amsg, (long long)"': expected "); + amsg = string_concat(amsg, dt); + amsg = string_concat(amsg, (long long)" but got "); + amsg = string_concat(amsg, en_arg_type(get_list(cargs, pi), vk, vo)); + ok = append_list(errs, amsg); + } + } + pi = (pi + 1LL); + } + fi = length_list(fk); + } else { + fi = (fi + 1LL); + } + } ci = 0LL; while (ci < length_list(cargs)) { - okc = en_check_expr(get_list(cargs, ci), errs, vk, vo, vf); + okc = en_check_expr(get_list(cargs, ci), errs, vk, vo, vf, fk, fp); ci = (ci + 1LL); } ret_val = 0LL; goto L_cleanup; } if (((((t == 18LL || t == 20LL) || t == 21LL) || t == 32LL) || t == 33LL)) { - ret_val = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf); + ret_val = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf, fk, fp); goto L_cleanup; } ret_val = 0LL; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(3); + ep_gc_pop_roots(8); return ret_val; } -long long en_check_stmts(long long stmts, long long errs, long long vk, long long vo, long long vf) { +long long en_check_stmts(long long stmts, long long errs, long long vk, long long vo, long long vf, long long fk, long long fp, long long drt) { long long i = 0; long long stmt = 0; long long t = 0; long long ok = 0; + long long rmsg = 0; long long eb = 0; long long arms = 0; long long ari = 0; long long ret_val = 0; + ep_gc_push_root(&rmsg); ep_gc_maybe_collect(); i = 0LL; @@ -9926,32 +10079,40 @@ long long en_check_stmts(long long stmts, long long errs, long long vk, long lon stmt = get_list(stmts, i); t = get_list(stmt, 0LL); if (t == 7LL) { - ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf); + ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp); + } + if (t == 8LL) { + if (en_type_conflict(drt, get_list(stmt, 1LL), vk, vo) == 1LL) { + rmsg = string_concat((long long)"return type mismatch: function is declared to return ", drt); + rmsg = string_concat(rmsg, (long long)" but returns "); + rmsg = string_concat(rmsg, en_arg_type(get_list(stmt, 1LL), vk, vo)); + ok = append_list(errs, rmsg); + } } if (((t == 8LL || t == 9LL) || t == 36LL)) { - ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf); + ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf, fk, fp); } if (t == 10LL) { - ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf); - ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf); + ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf, fk, fp); + ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp, drt); eb = get_list(stmt, 3LL); if (eb != 0LL) { - ok = en_check_stmts(eb, errs, vk, vo, vf); + ok = en_check_stmts(eb, errs, vk, vo, vf, fk, fp, drt); } } if (t == 11LL) { - ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf); - ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf); + ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf, fk, fp); + ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp, drt); } if (t == 28LL) { - ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf); - ok = en_check_stmts(get_list(stmt, 3LL), errs, vk, vo, vf); + ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp); + ok = en_check_stmts(get_list(stmt, 3LL), errs, vk, vo, vf, fk, fp, drt); } if (t == 27LL) { arms = get_list(stmt, 2LL); ari = 0LL; while (ari < length_list(arms)) { - ok = en_check_stmts(get_list(get_list(arms, ari), 2LL), errs, vk, vo, vf); + ok = en_check_stmts(get_list(get_list(arms, ari), 2LL), errs, vk, vo, vf, fk, fp, drt); ari = (ari + 1LL); } } @@ -9960,6 +10121,7 @@ long long en_check_stmts(long long stmts, long long errs, long long vk, long lon ret_val = 0LL; goto L_cleanup; L_cleanup: + ep_gc_pop_roots(1); return ret_val; } @@ -9979,8 +10141,18 @@ long long check_program(long long program) { long long fields = 0; long long fi = 0; long long ok = 0; + long long en_fk = 0; + long long en_fp = 0; long long en_funcs = 0; + long long bfi = 0; + long long bfn = 0; + long long pts = 0; + long long bpl = 0; + long long bpi = 0; + long long ptname = 0; long long efi = 0; + long long efn = 0; + long long edrt = 0; long long en_methods = 0; long long emi = 0; long long funcs = 0; @@ -10000,6 +10172,10 @@ long long check_program(long long program) { ep_gc_push_root(&en_vo); ep_gc_push_root(&en_vf); ep_gc_push_root(&fts); + ep_gc_push_root(&en_fk); + ep_gc_push_root(&en_fp); + ep_gc_push_root(&pts); + ep_gc_push_root(&edrt); ep_gc_maybe_collect(); errs = create_list(); @@ -10031,17 +10207,42 @@ long long check_program(long long program) { ei = (ei + 1LL); } } + en_fk = create_list(); + en_fp = create_list(); en_funcs = get_list(program, 3LL); + bfi = 0LL; + while (bfi < length_list(en_funcs)) { + bfn = get_list(en_funcs, bfi); + pts = create_list(); + bpl = get_list(bfn, 2LL); + bpi = 0LL; + while (bpi < length_list(bpl)) { + ptname = (long long)""; + if (length_list(get_list(bpl, bpi)) > 2LL) { + ptname = get_list(get_list(bpl, bpi), 2LL); + } + ok = append_list(pts, ptname); + bpi = (bpi + 1LL); + } + ok = append_list(en_fk, get_list(bfn, 1LL)); + ok = append_list(en_fp, pts); + bfi = (bfi + 1LL); + } efi = 0LL; while (efi < length_list(en_funcs)) { - ok = en_check_stmts(get_list(get_list(en_funcs, efi), 3LL), errs, en_vk, en_vo, en_vf); + efn = get_list(en_funcs, efi); + edrt = (long long)""; + if (length_list(efn) > 5LL) { + edrt = string_concat(get_list(efn, 5LL), (long long)""); + } + ok = en_check_stmts(get_list(efn, 3LL), errs, en_vk, en_vo, en_vf, en_fk, en_fp, edrt); efi = (efi + 1LL); } if (length_list(program) > 6LL) { en_methods = get_list(program, 6LL); emi = 0LL; while (emi < length_list(en_methods)) { - ok = en_check_stmts(get_list(get_list(en_methods, emi), 4LL), errs, en_vk, en_vo, en_vf); + ok = en_check_stmts(get_list(get_list(en_methods, emi), 4LL), errs, en_vk, en_vo, en_vf, en_fk, en_fp, (long long)""); emi = (emi + 1LL); } } @@ -10075,7 +10276,7 @@ long long check_program(long long program) { ret_val = 0LL; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(5); + ep_gc_pop_roots(9); return ret_val; } @@ -13592,9 +13793,9 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon if (type == 32LL) { inner = get_list(expr, 1LL); inner_str = gen_expr(state, inner, var_keys, var_values); - res = (long long)"(!"; + res = (long long)"(!("; res = string_concat(res, inner_str); - res = string_concat(res, (long long)")"); + res = string_concat(res, (long long)"))"); ret_val = res; goto L_cleanup; } diff --git a/ep_check.ep b/ep_check.ep index b8ec1a7..a00c575 100644 --- a/ep_check.ep +++ b/ep_check.ep @@ -165,7 +165,38 @@ define en_field_type_at with variant and ai and vk and vf: set i to i + 1 return "" -define en_check_expr with expr and errs and vk and vo and vf: +# 1 when an expression whose coarse type is statically known conflicts with the +# declared primitive type name `dt` in a way that is never runtime-safe: +# a Str where Int/Float/Bool is declared, or a non-zero literal where Str is +# declared. The literal 0 is exempt everywhere (the null idiom). +define en_type_conflict with dt and arg and vk and vo: + set dts to string_concat(dt and "") + if string_length(dts) == 0: + return 0 + set at to string_concat(en_arg_type(arg and vk and vo) and "") + if string_length(at) == 0: + return 0 + if dts equals "Str": + if at equals "Int": + if get_list(arg and 0) == 1: + if get_list(arg and 1) == 0: + return 0 + return 1 + if at equals "Float": + return 1 + if at equals "Bool": + return 1 + return 0 + if at equals "Str": + if dts equals "Int": + return 1 + if dts equals "Float": + return 1 + if dts equals "Bool": + return 1 + return 0 + +define en_check_expr with expr and errs and vk and vo and vf and fk and fp: if expr == 0: return 0 set t to get_list(expr and 0) @@ -186,50 +217,100 @@ define en_check_expr with expr and errs and vk and vo and vf: set msg to string_concat(msg and " but got ") set msg to string_concat(msg and at) set ok to append_list(errs and msg) - set oka to en_check_expr(arg and errs and vk and vo and vf) + set oka to en_check_expr(arg and errs and vk and vo and vf and fk and fp) set ai to ai + 1 return 0 - if t == 4 || t == 5 || t == 14: - set okl to en_check_expr(get_list(expr and 1) and errs and vk and vo and vf) - set okr to en_check_expr(get_list(expr and 3) and errs and vk and vo and vf) + if t == 4: # BINARY arithmetic — a Str operand is only legal as the `+ 0` cast + set bl to get_list(expr and 1) + set bop to get_list(expr and 2) + set br to get_list(expr and 3) + set blt to string_concat(en_arg_type(bl and vk and vo) and "") + set brt to string_concat(en_arg_type(br and vk and vo) and "") + if blt equals "Str": + set castok to 0 + if bop == 1: # OP_ADD + if get_list(br and 0) == 1: + if get_list(br and 1) == 0: + set castok to 1 + if castok == 0: + set ok to append_list(errs and "arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom") + if brt equals "Str": + set castok2 to 0 + if bop == 1: + if get_list(bl and 0) == 1: + if get_list(bl and 1) == 0: + set castok2 to 1 + if castok2 == 0: + set ok to append_list(errs and "arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom") + set okl to en_check_expr(bl and errs and vk and vo and vf and fk and fp) + set okr to en_check_expr(br and errs and vk and vo and vf and fk and fp) + return 0 + if t == 5 || t == 14: + set okl to en_check_expr(get_list(expr and 1) and errs and vk and vo and vf and fk and fp) + set okr to en_check_expr(get_list(expr and 3) and errs and vk and vo and vf and fk and fp) return 0 - if t == 6: # CALL + if t == 6: # CALL — check each argument against the declared parameter type + set cname to string_concat(get_list(expr and 1) and "") set cargs to get_list(expr and 2) + set fi to 0 + repeat while fi < length_list(fk): + if cname equals get_list(fk and fi): + set pi to 0 + repeat while pi < length_list(cargs): + if pi < length_list(get_list(fp and fi)): + set dt to string_concat(get_list(get_list(fp and fi) and pi) and "") + if en_type_conflict(dt and get_list(cargs and pi) and vk and vo) == 1: + set amsg to string_concat("argument type mismatch in call to '" and cname) + set amsg to string_concat(amsg and "': expected ") + set amsg to string_concat(amsg and dt) + set amsg to string_concat(amsg and " but got ") + set amsg to string_concat(amsg and en_arg_type(get_list(cargs and pi) and vk and vo)) + set ok to append_list(errs and amsg) + set pi to pi + 1 + set fi to length_list(fk) + else: + set fi to fi + 1 set ci to 0 repeat while ci < length_list(cargs): - set okc to en_check_expr(get_list(cargs and ci) and errs and vk and vo and vf) + set okc to en_check_expr(get_list(cargs and ci) and errs and vk and vo and vf and fk and fp) set ci to ci + 1 return 0 if t == 18 || t == 20 || t == 21 || t == 32 || t == 33: - return en_check_expr(get_list(expr and 1) and errs and vk and vo and vf) + return en_check_expr(get_list(expr and 1) and errs and vk and vo and vf and fk and fp) return 0 -define en_check_stmts with stmts and errs and vk and vo and vf: +define en_check_stmts with stmts and errs and vk and vo and vf and fk and fp and drt: set i to 0 repeat while i < length_list(stmts): set stmt to get_list(stmts and i) set t to get_list(stmt and 0) if t == 7: - set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf) + set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp) + if t == 8: # RETURN — a literal of the wrong primitive type is a hard error + if en_type_conflict(drt and get_list(stmt and 1) and vk and vo) == 1: + set rmsg to string_concat("return type mismatch: function is declared to return " and drt) + set rmsg to string_concat(rmsg and " but returns ") + set rmsg to string_concat(rmsg and en_arg_type(get_list(stmt and 1) and vk and vo)) + set ok to append_list(errs and rmsg) if t == 8 || t == 9 || t == 36: - set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf) + set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf and fk and fp) if t == 10: - set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf) - set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf) + set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf and fk and fp) + set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp and drt) set eb to get_list(stmt and 3) if eb != 0: - set ok to en_check_stmts(eb and errs and vk and vo and vf) + set ok to en_check_stmts(eb and errs and vk and vo and vf and fk and fp and drt) if t == 11: - set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf) - set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf) + set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf and fk and fp) + set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp and drt) if t == 28: - set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf) - set ok to en_check_stmts(get_list(stmt and 3) and errs and vk and vo and vf) + set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp) + set ok to en_check_stmts(get_list(stmt and 3) and errs and vk and vo and vf and fk and fp and drt) if t == 27: set arms to get_list(stmt and 2) set ari to 0 repeat while ari < length_list(arms): - set ok to en_check_stmts(get_list(get_list(arms and ari) and 2) and errs and vk and vo and vf) + set ok to en_check_stmts(get_list(get_list(arms and ari) and 2) and errs and vk and vo and vf and fk and fp and drt) set ari to ari + 1 set i to i + 1 return 0 @@ -264,16 +345,39 @@ define check_program with program: set ok to append_list(en_vf and fts) set evi to evi + 1 set ei to ei + 1 + # Function name -> declared parameter type names (parallel lists), read from + # the param nodes' captured annotations ([name, is_borrow, type_name]). + set en_fk to create_list() + set en_fp to create_list() set en_funcs to get_list(program and 3) + set bfi to 0 + repeat while bfi < length_list(en_funcs): + set bfn to get_list(en_funcs and bfi) + set pts to create_list() + set bpl to get_list(bfn and 2) + set bpi to 0 + repeat while bpi < length_list(bpl): + set ptname to "" + if length_list(get_list(bpl and bpi)) > 2: + set ptname to get_list(get_list(bpl and bpi) and 2) + set ok to append_list(pts and ptname) + set bpi to bpi + 1 + set ok to append_list(en_fk and get_list(bfn and 1)) + set ok to append_list(en_fp and pts) + set bfi to bfi + 1 set efi to 0 repeat while efi < length_list(en_funcs): - set ok to en_check_stmts(get_list(get_list(en_funcs and efi) and 3) and errs and en_vk and en_vo and en_vf) + set efn to get_list(en_funcs and efi) + set edrt to "" + if length_list(efn) > 5: + set edrt to string_concat(get_list(efn and 5) and "") + set ok to en_check_stmts(get_list(efn and 3) and errs and en_vk and en_vo and en_vf and en_fk and en_fp and edrt) set efi to efi + 1 if length_list(program) > 6: set en_methods to get_list(program and 6) set emi to 0 repeat while emi < length_list(en_methods): - set ok to en_check_stmts(get_list(get_list(en_methods and emi) and 4) and errs and en_vk and en_vo and en_vf) + set ok to en_check_stmts(get_list(get_list(en_methods and emi) and 4) and errs and en_vk and en_vo and en_vf and en_fk and en_fp and "") set emi to emi + 1 set funcs to get_list(program and 3) diff --git a/ep_codegen.ep b/ep_codegen.ep index 5c2ad18..f84fa55 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -2100,9 +2100,11 @@ define gen_expr with state and expr and var_keys and var_values: if type == 32: # NODE_UNARY_NOT set inner to get_list(expr and 1) set inner_str to gen_expr(state and inner and var_keys and var_values) - set res to "(!" + # The operand must be parenthesized: comparisons emit as bare `a > b`, + # and C gives `!` higher precedence, so `!a > b` would negate only `a`. + set res to "(!(" set res to string_concat(res and inner_str) - set res to string_concat(res and ")") + set res to string_concat(res and "))") return res if type == 33: # NODE_TRY diff --git a/ep_parser.ep b/ep_parser.ep index 0029222..676f6a2 100644 --- a/ep_parser.ep +++ b/ep_parser.ep @@ -463,14 +463,18 @@ define parse_param_list with state: set is_borrow to 1 set tok_param to advance_token(state) set param_name to get_token_value(tok_param) - # Optional type annotation: as Type + # Optional type annotation: as Type — captured (index 2 on the param + # node) so the semantic checker can verify call-site argument types. + set ptype to "" set next_as to peek_token(state) if get_token_type(next_as) == 42: # as set dummy to advance_token(state) - set dummy to advance_token(state) # consume type name + set tok_ptype to advance_token(state) # the type name + set ptype to get_token_value(tok_ptype) set param_node to create_list() + 0 set ok to append_list(param_node and param_name) set ok to append_list(param_node and is_borrow) + set ok to append_list(param_node and ptype) set ok to append_list(params and param_node) set loop to 1 repeat while loop == 1: @@ -484,14 +488,17 @@ define parse_param_list with state: set is_borrow3 to 1 set tok_param to advance_token(state) set param_name to get_token_value(tok_param) - # Optional type annotation + # Optional type annotation — captured like the first parameter's. + set ptype3 to "" set next_as to peek_token(state) if get_token_type(next_as) == 42: # as set dummy to advance_token(state) - set dummy to advance_token(state) + set tok_ptype3 to advance_token(state) + set ptype3 to get_token_value(tok_ptype3) set param_node to create_list() + 0 set ok to append_list(param_node and param_name) set ok to append_list(param_node and is_borrow3) + set ok to append_list(param_node and ptype3) set ok to append_list(params and param_node) else: set loop to 0 diff --git a/src/codegen.rs b/src/codegen.rs index 1f9b650..8e27e77 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -154,6 +154,9 @@ pub struct Codegen { list_element_types: HashMap, /// Maps closure variable name -> generated C function name closure_c_names: HashMap, + /// Every lifted-closure C function name ever emitted (never cleared): used to + /// keep names globally unique across functions and duplicated loop bodies. + emitted_closure_names: std::collections::HashSet, /// Set by the Set handler to pass the variable name to Closure codegen pending_closure_name: Option, /// Maps closure C function name -> list of captured variable names from outer scope @@ -182,6 +185,7 @@ impl Codegen { variant_to_enum: HashMap::new(), list_element_types: HashMap::new(), closure_c_names: HashMap::new(), + emitted_closure_names: std::collections::HashSet::new(), pending_closure_name: None, closure_captures: HashMap::new(), builtin_c_funcs: std::collections::HashSet::new(), @@ -2284,16 +2288,32 @@ impl Codegen { // Generate a static closure function // Use pre-registered name if this closure is being assigned to a named variable let closure_name = if let Some(var_name) = self.pending_closure_name.take() { - self.closure_c_names.get(&var_name) + let base = self.closure_c_names.get(&var_name) .cloned() - .unwrap_or_else(|| { - let name = format!("_ep_closure_{}", self.spawn_index); + .unwrap_or_else(|| format!("_ep_closure_{}", Self::sanitize_c_name(&var_name))); + // Every emission gets a globally unique C name: the same variable + // name in two functions (or the same closure duplicated by loop + // unrolling) must not produce colliding function definitions. + let unique = if self.emitted_closure_names.contains(&base) { + loop { + let candidate = format!("{}_{}", base, self.spawn_index); self.spawn_index += 1; - name - }) + if !self.emitted_closure_names.contains(&candidate) { + break candidate; + } + } + } else { + base + }; + self.emitted_closure_names.insert(unique.clone()); + // Later call sites resolve through this map, so point the + // variable at the name we actually emitted. + self.closure_c_names.insert(var_name, unique.clone()); + unique } else { let name = format!("_ep_closure_{}", self.spawn_index); self.spawn_index += 1; + self.emitted_closure_names.insert(name.clone()); name }; diff --git a/src/parser.rs b/src/parser.rs index 54a5c73..3672e40 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1150,11 +1150,19 @@ impl Parser { params.push(next_param); } self.expect(Token::Colon)?; - if self.peek() == &Token::Newline { - self.advance(); - } - // parse_block handles the Indent/Dedent - let body = self.parse_block()?; + let body = if self.peek() == &Token::Return { + // Single-line form: `given x: return x * 2` (documented in the + // README quick reference and accepted by the self-hosted parser). + self.advance(); // consume "return" + let ret = self.parse_expr(Precedence::Lowest)?; + vec![Stmt::new(StmtNode::Return(ret))] + } else { + if self.peek() == &Token::Newline { + self.advance(); + } + // parse_block handles the Indent/Dedent + self.parse_block()? + }; Expr::with_span(ExprNode::Closure(params, body), span) } Token::Borrow => { diff --git a/src/type_check.rs b/src/type_check.rs index 11c8fea..e1196da 100644 --- a/src/type_check.rs +++ b/src/type_check.rs @@ -1298,7 +1298,31 @@ impl TypeChecker { let ret_type = self.check_expr(expr); // Enforce the declared return type when the function annotates one. if let Some(declared) = self.current_return_type.clone() { - if unify(&mut self.subst, &ret_type, &declared, stmt.span).is_err() { + // A literal of a plainly wrong primitive type is never + // runtime-safe (a Str literal returned as Int prints a pointer): + // reject it outright. Everything subtler stays a warning below + // (the `return 0` null idiom, unresolved inference vars, etc.). + let literal_conflict = match (&expr.node, &declared) { + (ExprNode::StringLiteral(_), MonoType::Int) + | (ExprNode::StringLiteral(_), MonoType::Float) + | (ExprNode::StringLiteral(_), MonoType::Bool) + | (ExprNode::FloatLiteral(_), MonoType::Str) + | (ExprNode::BoolLiteral(_), MonoType::Str) => true, + (ExprNode::Integer(n), MonoType::Str) => *n != 0, // 0 = null idiom + _ => false, + }; + if literal_conflict { + let found = self.subst.apply(&ret_type); + let expected = self.subst.apply(&declared); + self.error_with_hint( + format!( + "Return type mismatch: function is declared to return {} but this returns {}", + expected.display_name(), found.display_name() + ), + stmt.span, + format!("Return a {} value, or change the function's declared return type.", expected.display_name()), + ); + } else if unify(&mut self.subst, &ret_type, &declared, stmt.span).is_err() { let found = self.subst.apply(&ret_type); let expected = self.subst.apply(&declared); // Downgraded from a hard error to a warning: the codegen @@ -1571,17 +1595,22 @@ impl TypeChecker { } MonoType::Float } else { - // For addition (+), allow Str/DynStr — this is the "pointer + 0" cast - // pattern used by the self-hosted compiler to coerce string pointers to int. - // For *, /, -, %: reject Str (e.g. "hello" * 2 is always a bug). + // A Str operand is allowed in addition ONLY as the "pointer + 0" + // cast idiom the self-hosted compiler uses to coerce a string + // pointer to int. Any other Str arithmetic (`"abc" + 5`, + // `"hello" * 2`) is C pointer arithmetic on a literal — always + // a bug — and is rejected. let is_add = matches!(op, Op::Add); - - let str_ok = |ty: &MonoType| -> bool { + let other_is_zero = |e: &Expr| matches!(e.node, ExprNode::Integer(0)); + let cast_idiom_l = is_add && other_is_zero(right); + let cast_idiom_r = is_add && other_is_zero(left); + + let str_ok = |ty: &MonoType, cast_idiom: bool| -> bool { matches!(ty, MonoType::Int | MonoType::Bool | MonoType::Any | MonoType::DynStr | MonoType::List(_)) - || (is_add && matches!(ty, MonoType::Str)) + || (cast_idiom && matches!(ty, MonoType::Str)) }; - if !str_ok(<_r) { + if !str_ok(<_r, cast_idiom_l) { if let Err(_) = unify(&mut self.subst, <, &MonoType::Int, expr.span) { self.error( format!("Left operand of arithmetic must be numeric, found {}", self.subst.apply(<).display_name()), @@ -1589,7 +1618,7 @@ impl TypeChecker { ); } } - if !str_ok(&rt_r) { + if !str_ok(&rt_r, cast_idiom_r) { if let Err(_) = unify(&mut self.subst, &rt, &MonoType::Int, expr.span) { self.error( format!("Right operand of arithmetic must be numeric, found {}", self.subst.apply(&rt).display_name()), diff --git a/tests/test_arg_type_mismatch.ep b/tests/test_arg_type_mismatch.ep new file mode 100644 index 0000000..4697d3b --- /dev/null +++ b/tests/test_arg_type_mismatch.ep @@ -0,0 +1,9 @@ +# expected_compile_error +# Passing a Str literal where an Int parameter is declared must be rejected by +# both compilers. +define double_it with n as Int returning Int: + return n * 2 + +define main: + display double_it("five") + return 0 diff --git a/tests/test_return_type_mismatch.ep b/tests/test_return_type_mismatch.ep index 5d81af7..4fee971 100644 --- a/tests/test_return_type_mismatch.ep +++ b/tests/test_return_type_mismatch.ep @@ -1,5 +1,7 @@ -# A function declared to return Int must not return a Str. -# Since return type mismatches are warnings, this will compile and run. +# expected_compile_error +# A function declared to return Int must not return a Str literal — this is +# never runtime-safe and is rejected outright (subtler mismatches, like the +# `return 0` null idiom, remain warnings). define bad_return returning Int: return "not an int" diff --git a/tests/test_return_type_mismatch.expected b/tests/test_return_type_mismatch.expected deleted file mode 100644 index d78b42e..0000000 --- a/tests/test_return_type_mismatch.expected +++ /dev/null @@ -1 +0,0 @@ -not an int diff --git a/tests/test_str_arithmetic.ep b/tests/test_str_arithmetic.ep new file mode 100644 index 0000000..c36da88 --- /dev/null +++ b/tests/test_str_arithmetic.ep @@ -0,0 +1,7 @@ +# expected_compile_error +# Arithmetic on a string literal is C pointer arithmetic in disguise — always a +# bug. The only sanctioned form is the `+ 0` int-cast idiom. +define main: + set s to "abc" + 5 + display s + return 0 From 1f8e69068676f88e9ad902a4527eda547ddc4fbc Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 09:12:13 +0100 Subject: [PATCH 48/51] fix(diagnostics): error hints match what the user wrote, not the parser's prose The fuzzy did-you-mean matcher ran against the error message's own words, so nearly every parse error suggested "Did you mean 'for'?" (Levenshtein-matching the word 'found'). It now matches only user-quoted text, and the most common mistakes get targeted plain-English hints: - commas between arguments -> shows the 'and' form: add(1 and 2) - missing ':' on a block line -> says which lines need ':' - inconsistent indentation -> says blocks must align - unknown statement start -> lists the statement keywords Rust suite 71/71. Co-Authored-By: Claude Fable 5 --- src/main.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index ff9d87d..526c6f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1705,15 +1705,26 @@ fn find_closest_match<'a>(target: &str, candidates: &[&'a str], max_distance: us } fn get_suggestion(message: &str) -> Option { - // Keep specific suggestions for known patterns - if message.contains("character: ','") { - return Some("In Ernos, function arguments are separated by 'and', not commas.".to_string()); + // Targeted, plain-English hints for the most common mistakes first. + if message.contains("character: ','") || message.contains("found Comma") { + return Some("In Ernos, arguments and list items are joined with 'and', not commas — e.g. add(1 and 2).".to_string()); } if message.contains("Unexpected statement start: Identifier") { return Some("Functions called as statements must be assigned to variables, e.g. 'set ok to func(...)'.".to_string()); } + if message.contains("Expected Colon") { + return Some("Lines that start a block — 'define', 'if', 'repeat while', 'for each' — must end with ':'.".to_string()); + } + if message.contains("Indentation error") { + return Some("Every statement in the same block must be indented by the same amount (4 spaces per level is conventional).".to_string()); + } + if message.contains("Unexpected statement start") { + return Some("Statements begin with a keyword like 'set', 'display', 'if', 'repeat while', 'for each', or 'return'.".to_string()); + } - // Try Levenshtein matching against all ErnosPlain keywords + // Fuzzy keyword matching — but ONLY against words the user actually wrote + // (quoted in the message). Matching against the message's own prose + // produced nonsense like `found` -> "Did you mean 'for'?". let keywords: &[&str] = &[ "set", "to", "define", "with", "return", "display", "if", "else", "repeat", "while", "for", "each", "in", @@ -1725,15 +1736,16 @@ fn get_suggestion(message: &str) -> Option { "import", "as", "returning", "of", "try", "from", "range", ]; - // Extract potential misspelled words from the error message - // Look for words in the message that might be misspellings - let words: Vec<&str> = message.split(|c: char| !c.is_alphanumeric() && c != '_') - .filter(|w| !w.is_empty() && w.len() >= 2) + let quoted: Vec<&str> = message + .split('\'') + .skip(1) + .step_by(2) + .filter(|w| !w.is_empty() && w.len() >= 2 && w.chars().all(|c| c.is_alphanumeric() || c == '_')) .collect(); - for word in &words { + for word in "ed { let lower = word.to_lowercase(); - let max_dist = if lower.len() <= 4 { 2 } else { 3 }; + let max_dist = if lower.len() <= 4 { 1 } else { 2 }; if let Some(closest) = find_closest_match(&lower, keywords, max_dist) { // Don't suggest if the word is already a keyword if lower != closest { From 560e607902f7774e3e5c06102f4f48b371744c51 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 09:21:56 +0100 Subject: [PATCH 49/51] perf(codegen): numeric functions skip GC rooting when args go to Int-declared params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GC-root analysis already skipped rooting for Int/Float/Bool locals used only in primitive expressions, but treated ANY user-function call as non-primitive usage — so fib(n - 1) forced n to be rooted and the function to run push_root/maybe_collect/pop_roots on every call: measured 6.3x slower than C on fib(35). Passing a variable to a parameter DECLARED Int/Float/Bool is now primitive usage (new prim_param_flags map, threaded through the analysis). fib(35): 0.19s -> 0.03s user — identical to clang -O2 C. Safety: rooting only skipped when the callee's parameter is explicitly declared primitive; unannotated params keep conservative rooting. Verified: Rust suite 71/71, parity 53/53 + 12/12 rejections, byte-identical fixpoint, clang-only bootstrap verify, GC stress tests (remembered-set UAF, oldgen) ASAN-clean, zero warnings. Co-Authored-By: Claude Fable 5 --- src/codegen.rs | 67 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 8e27e77..5698ea0 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -57,7 +57,7 @@ fn expr_contains_var(expr: &Expr, var_name: &str) -> bool { } } -fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool { +fn is_primitive_op_expr(expr: &Expr, param_name: &str, prim_params: &HashMap>) -> bool { match &expr.node { ExprNode::Integer(_) | ExprNode::FloatLiteral(_) | ExprNode::BoolLiteral(_) | ExprNode::StringLiteral(_) => true, ExprNode::Identifier(_name) => { @@ -65,10 +65,10 @@ fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool { true } ExprNode::Binary(l, _, r) | ExprNode::Comparison(l, _, r) | ExprNode::Logical(l, _, r) => { - is_primitive_op_expr(l, param_name) && is_primitive_op_expr(r, param_name) + is_primitive_op_expr(l, param_name, prim_params) && is_primitive_op_expr(r, param_name, prim_params) } ExprNode::UnaryNot(inner) => { - is_primitive_op_expr(inner, param_name) + is_primitive_op_expr(inner, param_name, prim_params) } ExprNode::Call(name, args) => { let primitive_arg_builtins = [ @@ -76,7 +76,19 @@ fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool { "ep_sleep_ms", "sleep_ms", "ep_time_ms", "ep_random_int" ]; if primitive_arg_builtins.contains(&name.as_str()) { - args.iter().all(|arg| is_primitive_op_expr(arg, param_name)) + args.iter().all(|arg| is_primitive_op_expr(arg, param_name, prim_params)) + } else if let Some(flags) = prim_params.get(name.as_str()) { + // User-defined callee: passing this variable into a parameter that + // is DECLARED Int/Float/Bool keeps it primitive — the callee treats + // it as a number, so no GC root is needed here (e.g. fib(n - 1)). + args.iter().enumerate().all(|(i, arg)| { + if expr_contains_var(arg, param_name) { + flags.get(i).copied().unwrap_or(false) + && is_primitive_op_expr(arg, param_name, prim_params) + } else { + true + } + }) } else { !expr_contains_var(expr, param_name) } @@ -87,27 +99,27 @@ fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool { } } -fn stmt_contains_non_primitive_usage(stmt: &Stmt, param_name: &str) -> bool { +fn stmt_contains_non_primitive_usage(stmt: &Stmt, param_name: &str, prim_params: &HashMap>) -> bool { match &stmt.node { StmtNode::Set(_, expr, _) => { - !is_primitive_op_expr(expr, param_name) + !is_primitive_op_expr(expr, param_name, prim_params) } StmtNode::If(cond, then_b, else_b) => { - if !is_primitive_op_expr(cond, param_name) { return true; } - then_b.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name)) || - else_b.as_ref().map_or(false, |eb| eb.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name))) + if !is_primitive_op_expr(cond, param_name, prim_params) { return true; } + then_b.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params)) || + else_b.as_ref().map_or(false, |eb| eb.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params))) } StmtNode::RepeatWhile(cond, body) => { - if !is_primitive_op_expr(cond, param_name) { return true; } - body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name)) + if !is_primitive_op_expr(cond, param_name, prim_params) { return true; } + body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params)) } StmtNode::ForEach(loop_var, iterable, body) => { if loop_var == param_name { return true; } - if !is_primitive_op_expr(iterable, param_name) { return true; } - body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name)) + if !is_primitive_op_expr(iterable, param_name, prim_params) { return true; } + body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params)) } StmtNode::Return(expr) | StmtNode::Display(expr) | StmtNode::ExprStmt(expr) => { - !is_primitive_op_expr(expr, param_name) + !is_primitive_op_expr(expr, param_name, prim_params) } StmtNode::Send(expr, body) => { expr_contains_var(expr, param_name) || expr_contains_var(body, param_name) @@ -119,7 +131,7 @@ fn stmt_contains_non_primitive_usage(stmt: &Stmt, param_name: &str) -> bool { if expr_contains_var(expr, param_name) { return true; } for (_, bindings, body) in arms { if bindings.contains(¶m_name.to_string()) { return true; } - if body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name)) { return true; } + if body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params)) { return true; } } false } @@ -157,6 +169,10 @@ pub struct Codegen { /// Every lifted-closure C function name ever emitted (never cleared): used to /// keep names globally unique across functions and duplicated loop bodies. emitted_closure_names: std::collections::HashSet, + /// Function name -> per-parameter "declared Int/Float/Bool" flags. Lets the + /// GC-root analysis treat passing an Int var to an Int-declared parameter as + /// primitive usage (no rooting), so numeric hot paths pay zero GC overhead. + prim_param_flags: HashMap>, /// Set by the Set handler to pass the variable name to Closure codegen pending_closure_name: Option, /// Maps closure C function name -> list of captured variable names from outer scope @@ -186,6 +202,7 @@ impl Codegen { list_element_types: HashMap::new(), closure_c_names: HashMap::new(), emitted_closure_names: std::collections::HashSet::new(), + prim_param_flags: HashMap::new(), pending_closure_name: None, closure_captures: HashMap::new(), builtin_c_funcs: std::collections::HashSet::new(), @@ -570,6 +587,16 @@ impl Codegen { } } + // Per-parameter "declared primitive" flags for every user function, used + // by the GC-root analysis: passing an Int var to an Int-declared + // parameter is primitive usage and needs no root. + for func in &program.functions { + let flags: Vec = func.params.iter().map(|p| { + matches!(p.2, Some(TypeAnnotation::Int) | Some(TypeAnnotation::Float) | Some(TypeAnnotation::Bool)) + }).collect(); + self.prim_param_flags.insert(func.name.clone(), flags); + } + // Fixed-point iteration for resolution of dependencies/mutual calls let mut changed = true; let mut pass = 0; @@ -3592,7 +3619,7 @@ int main(int argc, char** argv) {{ let t = var_types.get(var_name); let should_root = t.map(|ty| { if !needs_gc_root(ty) { - func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name)) + func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name, &self.prim_param_flags)) } else { true } @@ -3608,7 +3635,7 @@ int main(int argc, char** argv) {{ let t = var_types.get(¶m.0); let should_root = t.map(|ty| { if !needs_gc_root(ty) { - func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0)) + func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0, &self.prim_param_flags)) } else { true } @@ -3884,7 +3911,7 @@ int main(int argc, char** argv) {{ let t = var_types.get(var_name); let should_root = t.map(|ty| { if !needs_gc_root(ty) { - md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name)) + md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name, &self.prim_param_flags)) } else { true } @@ -3900,7 +3927,7 @@ int main(int argc, char** argv) {{ let t = var_types.get("self"); let should_root = t.map(|ty| { if !needs_gc_root(ty) { - md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, "self")) + md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, "self", &self.prim_param_flags)) } else { true } @@ -3915,7 +3942,7 @@ int main(int argc, char** argv) {{ let t = var_types.get(¶m.0); let should_root = t.map(|ty| { if !needs_gc_root(ty) { - md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0)) + md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0, &self.prim_param_flags)) } else { true } From c821d5baeacbf0006c278a4203cde7d3b6ab1728 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 09:25:21 +0100 Subject: [PATCH 50/51] test+ci: permanent differential suite (37 programs), real gates in CI, docs truth-up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tests/run_differential.sh + tests/differential/: 37 adversarial programs (operator precedence, GC/closure corner cases, unicode/string edges, rejection agreement) compiled by BOTH compilers, outputs diffed. This is the suite that caught the `not` miscompilation, the closure grammar drift, and the three type-safety holes. 37/37 agree. - CI now runs the full gate matrix on macOS + Linux: run_tests.sh, parity, differential, 3-stage fixpoint, and the clang-only bootstrap verify — previously it only built and smoke-tested a handful of files. - README/AGENT.md: current numbers (71/71, 53/53 + 12/12, 37/37), the differential gate documented, and an honest measured benchmark: fib(35) Ernos 0.03s vs C 0.03s user time (after the GC-rooting optimization). Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 78 +++++++-------------- AGENT.md | 3 +- README.md | 31 +++++--- tests/differential/bad_arg_count.ep | 5 ++ tests/differential/bad_arg_type.ep | 5 ++ tests/differential/bad_field_access.ep | 9 +++ tests/differential/bad_return_type.ep | 5 ++ tests/differential/bad_str_plus_int.ep | 4 ++ tests/differential/bad_undefined_func.ep | 3 + tests/differential/bad_undefined_var.ep | 3 + tests/differential/bad_unknown_variant.ep | 7 ++ tests/differential/big_concat.ep | 8 +++ tests/differential/bool_english.ep | 12 ++++ tests/differential/break_continue.ep | 11 +++ tests/differential/closure_counter.ep | 14 ++++ tests/differential/closure_in_loop_block.ep | 11 +++ tests/differential/closure_same_name.ep | 14 ++++ tests/differential/deep_recursion.ep | 8 +++ tests/differential/div_zero.ep | 7 ++ tests/differential/enum_nofield.ep | 24 +++++++ tests/differential/float_precision.ep | 11 +++ tests/differential/for_each_mutate.ep | 11 +++ tests/differential/fstring_edge.ep | 8 +++ tests/differential/int_overflow.ep | 6 ++ tests/differential/list_oob.ep | 5 ++ tests/differential/map_stress.ep | 17 +++++ tests/differential/match_nested.ep | 17 +++++ tests/differential/neg_arith.ep | 10 +++ tests/differential/nested_lists.ep | 10 +++ tests/differential/not_bool.ep | 8 +++ tests/differential/not_eq_int.ep | 8 +++ tests/differential/not_eq_paren.ep | 6 ++ tests/differential/not_lt.ep | 8 +++ tests/differential/not_str_eq.ep | 6 ++ tests/differential/pop_empty.ep | 5 ++ tests/differential/shortcircuit.ep | 10 +++ tests/differential/str_empty.ep | 9 +++ tests/differential/str_escapes.ep | 6 ++ tests/differential/str_unicode.ep | 6 ++ tests/differential/string_compare.ep | 11 +++ tests/run_differential.sh | 51 ++++++++++++++ 41 files changed, 426 insertions(+), 65 deletions(-) create mode 100644 tests/differential/bad_arg_count.ep create mode 100644 tests/differential/bad_arg_type.ep create mode 100644 tests/differential/bad_field_access.ep create mode 100644 tests/differential/bad_return_type.ep create mode 100644 tests/differential/bad_str_plus_int.ep create mode 100644 tests/differential/bad_undefined_func.ep create mode 100644 tests/differential/bad_undefined_var.ep create mode 100644 tests/differential/bad_unknown_variant.ep create mode 100644 tests/differential/big_concat.ep create mode 100644 tests/differential/bool_english.ep create mode 100644 tests/differential/break_continue.ep create mode 100644 tests/differential/closure_counter.ep create mode 100644 tests/differential/closure_in_loop_block.ep create mode 100644 tests/differential/closure_same_name.ep create mode 100644 tests/differential/deep_recursion.ep create mode 100644 tests/differential/div_zero.ep create mode 100644 tests/differential/enum_nofield.ep create mode 100644 tests/differential/float_precision.ep create mode 100644 tests/differential/for_each_mutate.ep create mode 100644 tests/differential/fstring_edge.ep create mode 100644 tests/differential/int_overflow.ep create mode 100644 tests/differential/list_oob.ep create mode 100644 tests/differential/map_stress.ep create mode 100644 tests/differential/match_nested.ep create mode 100644 tests/differential/neg_arith.ep create mode 100644 tests/differential/nested_lists.ep create mode 100644 tests/differential/not_bool.ep create mode 100644 tests/differential/not_eq_int.ep create mode 100644 tests/differential/not_eq_paren.ep create mode 100644 tests/differential/not_lt.ep create mode 100644 tests/differential/not_str_eq.ep create mode 100644 tests/differential/pop_empty.ep create mode 100644 tests/differential/shortcircuit.ep create mode 100644 tests/differential/str_empty.ep create mode 100644 tests/differential/str_escapes.ep create mode 100644 tests/differential/str_unicode.ep create mode 100644 tests/differential/string_compare.ep create mode 100755 tests/run_differential.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4afe498..9689bce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,43 +21,23 @@ jobs: - name: Run unit tests run: cargo test --release - - name: Syntax check — test_core.ep - run: cargo run --release -- --check tests/test_core.ep + - name: Full test suite (run_tests.sh) + run: ./run_tests.sh - - name: Syntax check — test_strings.ep - run: cargo run --release -- --check tests/test_strings.ep + - name: Build the self-hosted compiler (epc) + run: ./target/release/ernos epc.ep - - name: Syntax check — test_math.ep - run: cargo run --release -- --check tests/test_math.ep + - name: Self-hosted parity + rejection suite + run: bash tests/run_epc_parity.sh - - name: Syntax check — test_data.ep - run: cargo run --release -- --check tests/test_data.ep + - name: Differential suite (ernos vs epc must agree) + run: bash tests/run_differential.sh - - name: Compile and run test_core.ep - run: | - cargo run --release -- tests/test_core.ep - ./tests/test_core - - - name: Compile and run test_strings.ep - run: | - cargo run --release -- tests/test_strings.ep - ./tests/test_strings - - - name: Compile and run test_math.ep - run: | - cargo run --release -- tests/test_math.ep - ./tests/test_math - - - name: Compile and run test_float_ffi.ep - run: | - cargo run --release -- tests/test_float_ffi.ep - ./tests/test_float_ffi - - - name: Compile demo — creature_quest_demo5.ep - run: cargo run --release -- demos/creature_quest_demo5.ep + - name: 3-stage self-compilation fixpoint (gen2 == gen3) + run: bash tests/run_fixpoint.sh - - name: Syntax check — epc.ep (self-hosted compiler) - run: cargo run --release -- --check epc.ep + - name: Clang-only bootstrap (no Rust) — build, fixpoint, freshness + run: bash bootstrap/verify.sh - name: Run conformance tests run: | @@ -90,7 +70,7 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Install build dependencies - run: sudo apt-get update && sudo apt-get install -y gcc + run: sudo apt-get update && sudo apt-get install -y clang - name: Build (release) run: cargo build --release @@ -98,31 +78,23 @@ jobs: - name: Run unit tests run: cargo test --release - - name: Syntax check — test_core.ep - run: cargo run --release -- --check tests/test_core.ep + - name: Full test suite (run_tests.sh) + run: ./run_tests.sh - - name: Compile and run test_core.ep - run: | - cargo run --release -- tests/test_core.ep - ./tests/test_core + - name: Build the self-hosted compiler (epc) + run: ./target/release/ernos epc.ep - - name: Compile and run test_strings.ep - run: | - cargo run --release -- tests/test_strings.ep - ./tests/test_strings + - name: Self-hosted parity + rejection suite + run: bash tests/run_epc_parity.sh - - name: Compile and run test_math.ep - run: | - cargo run --release -- tests/test_math.ep - ./tests/test_math + - name: Differential suite (ernos vs epc must agree) + run: bash tests/run_differential.sh - - name: Compile and run test_float_ffi.ep - run: | - cargo run --release -- tests/test_float_ffi.ep - ./tests/test_float_ffi + - name: 3-stage self-compilation fixpoint (gen2 == gen3) + run: bash tests/run_fixpoint.sh - - name: Syntax check — epc.ep (self-hosted compiler) - run: cargo run --release -- --check epc.ep + - name: Clang-only bootstrap (no Rust) — build, fixpoint, freshness + run: bash bootstrap/verify.sh - name: Run conformance tests run: | diff --git a/AGENT.md b/AGENT.md index 10af701..70c77f0 100644 --- a/AGENT.md +++ b/AGENT.md @@ -60,10 +60,11 @@ cargo run -- epc.ep && ./epc tests/test_basic_math.ep && ./test_basic_math # Stronger gates (run after any epc-visible change): bash tests/run_fixpoint.sh # 3-stage byte-identical self-compile bash tests/run_epc_parity.sh # self-hosted coverage scoreboard (must not regress) +bash tests/run_differential.sh # both compilers must AGREE on 37 adversarial programs bash bootstrap/verify.sh # clang-only, Rust-free 3-stage fixpoint + parity + freshness ``` -Current state of these gates: `run_tests.sh` **69/69**, `run_epc_parity.sh` **54/54 runnable + 9/9 compile-error rejections** (0 wrongly accepted), `run_fixpoint.sh` byte-identical, `bootstrap/verify.sh` green. +Current state of these gates: `run_tests.sh` **71/71**, `run_epc_parity.sh` **53/53 runnable + 12/12 compile-error rejections** (0 wrongly accepted), `run_differential.sh` **37/37 agree**, `run_fixpoint.sh` byte-identical, `bootstrap/verify.sh` green. The self-hosted compiler is ~6,400 lines of real ErnosPlain — `ep_lexer.ep` (lexer), `ep_parser.ep` (parser), `ep_check.ep` (semantic checker), `ep_optimizer.ep` (constant folding + DCE), `ep_codegen.ep` (C codegen), `epc.ep` (driver) — that exercises the full type system, all builtin functions, list/string operations, struct creation, pattern matching, closures, floats, traits, `try`/Result, and coroutine async. If it doesn't compile, you broke something. diff --git a/README.md b/README.md index 7dbcc7b..17c91f7 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@

Version - Rust suite 69/69 - Self-hosted parity 54/54 - Rejection gate 9/9 + Rust suite 71/71 + Self-hosted parity 53/53 + Rejection gate 12/12 Clang-only bootstrap Byte-identical fixpoint Platform @@ -40,7 +40,7 @@ Ernos ships **two** complete compilers for the same language: - **`ernos`** — the reference compiler, written in Rust (~30k lines). - **`epc`** — the self-hosted compiler, **written entirely in Ernos** (`ep_lexer.ep`, `ep_parser.ep`, `ep_check.ep`, `ep_optimizer.ep`, `ep_codegen.ep`, `epc.ep`). -`epc` compiles **every one of the 54 runnable test programs**, rejects **all 9** compile-error tests through its own semantic checker, and — compiling its own source — reaches a **byte-identical fixpoint** (`gen2 == gen3`). A frozen C snapshot (`bootstrap/epc_bootstrap.c`) means the whole toolchain rebuilds from **clang alone** — no Rust, no `cargo`, no bootstrap chicken-and-egg. This is verified end-to-end on every change, **with zero disclosed caveats**. +`epc` compiles **every one of the 53 runnable test programs**, rejects **all 12** compile-error tests through its own semantic checker, and — compiling its own source — reaches a **byte-identical fixpoint** (`gen2 == gen3`). A frozen C snapshot (`bootstrap/epc_bootstrap.c`) means the whole toolchain rebuilds from **clang alone** — no Rust, no `cargo`, no bootstrap chicken-and-egg. This is verified end-to-end on every change, **with zero disclosed caveats**. > `clang bootstrap/epc_bootstrap.c -o epc && ./epc epc.ep` → a working compiler that recompiles itself and passes the full suite. @@ -59,7 +59,14 @@ Ernos ships **two** complete compilers for the same language: ### Performance -Ernos compiles to C, then to a native binary via `clang -O2`. The generated code has no interpreter overhead — it runs at the same speed as equivalent C. +Ernos compiles to C, then to a native binary via `clang -O2`. The generated code has no interpreter overhead, and numeric functions whose parameters are declared `Int`/`Float`/`Bool` pay **zero** GC bookkeeping — the compiler proves they can't touch the heap and strips the root registration entirely. + +Measured on Apple Silicon (`fib(35)`, naive doubly-recursive): + +| | user time | +|---|---| +| Ernos (`ernos fib.ep`) | **0.03 s** | +| C (`clang -O2 fib.c`) | **0.03 s** | --- @@ -370,9 +377,10 @@ The self-hosted pipeline is a full `lex → parse → **check** → **optimize** | Gate | Result | Verified by | |------|--------|-------------| -| Reference-compiler suite | **69 / 69** | `./run_tests.sh` | -| Self-hosted parity (runnable programs `epc` compiles + runs correctly) | **54 / 54** | `bash tests/run_epc_parity.sh` | -| Compile-error gate (programs `epc`'s checker must reject) | **9 / 9**, 0 wrongly accepted | `bash tests/run_epc_parity.sh` | +| Reference-compiler suite | **71 / 71** | `./run_tests.sh` | +| Self-hosted parity (runnable programs `epc` compiles + runs correctly) | **53 / 53** | `bash tests/run_epc_parity.sh` | +| Compile-error gate (programs `epc`'s checker must reject) | **12 / 12**, 0 wrongly accepted | `bash tests/run_epc_parity.sh` | +| Differential suite (both compilers agree on 37 adversarial programs) | **37 / 37** | `bash tests/run_differential.sh` | | 3-stage self-compilation fixpoint (`gen2 == gen3`, byte-identical) | **OK** | `bash tests/run_fixpoint.sh` | | Rust-free, clang-only bootstrap → recompile → fixpoint → full suite | **OK** | `bash bootstrap/verify.sh` | | Cargo build warnings | **0** | `cargo build --release` | @@ -402,13 +410,14 @@ The Rust compiler builds the same self-hosted compiler and remains the home of t ## The Test Matrix ```bash -./run_tests.sh # reference (Rust) compiler: 69/69 -bash tests/run_epc_parity.sh # self-hosted: 54/54 runnable + 9/9 rejections +./run_tests.sh # reference (Rust) compiler: 71/71 +bash tests/run_epc_parity.sh # self-hosted: 53/53 runnable + 12/12 rejections +bash tests/run_differential.sh # both compilers agree on 37 adversarial programs bash tests/run_fixpoint.sh # 3-stage byte-identical fixpoint bash bootstrap/verify.sh # clang-only, Rust-free end-to-end proof ``` -Every one of the 63 programs in `tests/` (54 runnable + 9 compile-error) is exercised by **both** compilers. Conformance tests live in [`conformance/`](conformance/); the formal grammar and type/memory/concurrency rules are in [`spec/ernos-spec.md`](spec/ernos-spec.md). +Every one of the 65 programs in `tests/` (53 runnable + 12 compile-error) is exercised by **both** compilers, and `tests/differential/` holds 37 adversarial programs (operator precedence, GC stress, closure corner cases, type-safety probes) on which the two compilers' compiled binaries must produce byte-identical output. Conformance tests live in [`conformance/`](conformance/); the formal grammar and type/memory/concurrency rules are in [`spec/ernos-spec.md`](spec/ernos-spec.md). --- diff --git a/tests/differential/bad_arg_count.ep b/tests/differential/bad_arg_count.ep new file mode 100644 index 0000000..3d9b3cc --- /dev/null +++ b/tests/differential/bad_arg_count.ep @@ -0,0 +1,5 @@ +define add with a as Int and b as Int returning Int: + return a + b +define main: + display add(1) + return 0 diff --git a/tests/differential/bad_arg_type.ep b/tests/differential/bad_arg_type.ep new file mode 100644 index 0000000..dcf98c2 --- /dev/null +++ b/tests/differential/bad_arg_type.ep @@ -0,0 +1,5 @@ +define double_it with n as Int returning Int: + return n * 2 +define main: + display double_it("five") + return 0 diff --git a/tests/differential/bad_field_access.ep b/tests/differential/bad_field_access.ep new file mode 100644 index 0000000..a885b93 --- /dev/null +++ b/tests/differential/bad_field_access.ep @@ -0,0 +1,9 @@ +define structure Point: + field x as Int + field y as Int +define main: + set p to create Point: + x is 1 + y is 2 + display p.z + return 0 diff --git a/tests/differential/bad_return_type.ep b/tests/differential/bad_return_type.ep new file mode 100644 index 0000000..e1427ec --- /dev/null +++ b/tests/differential/bad_return_type.ep @@ -0,0 +1,5 @@ +define halve with n as Int returning Int: + return "half" +define main: + display halve(4) + return 0 diff --git a/tests/differential/bad_str_plus_int.ep b/tests/differential/bad_str_plus_int.ep new file mode 100644 index 0000000..d93a096 --- /dev/null +++ b/tests/differential/bad_str_plus_int.ep @@ -0,0 +1,4 @@ +define main: + set s to "abc" + 5 + display s + return 0 diff --git a/tests/differential/bad_undefined_func.ep b/tests/differential/bad_undefined_func.ep new file mode 100644 index 0000000..a6e1169 --- /dev/null +++ b/tests/differential/bad_undefined_func.ep @@ -0,0 +1,3 @@ +define main: + display no_such_function(5) + return 0 diff --git a/tests/differential/bad_undefined_var.ep b/tests/differential/bad_undefined_var.ep new file mode 100644 index 0000000..814f52a --- /dev/null +++ b/tests/differential/bad_undefined_var.ep @@ -0,0 +1,3 @@ +define main: + display mystery_value + return 0 diff --git a/tests/differential/bad_unknown_variant.ep b/tests/differential/bad_unknown_variant.ep new file mode 100644 index 0000000..95306d3 --- /dev/null +++ b/tests/differential/bad_unknown_variant.ep @@ -0,0 +1,7 @@ +define choice Color: + variant Red + variant Green +define main: + set c to Purple + display 1 + return 0 diff --git a/tests/differential/big_concat.ep b/tests/differential/big_concat.ep new file mode 100644 index 0000000..9200a17 --- /dev/null +++ b/tests/differential/big_concat.ep @@ -0,0 +1,8 @@ +define main: + set s to "" + set i to 0 + repeat while i < 5000: + set s to concat(s and "ab") + set i to i + 1 + display string_length(s) + return 0 diff --git a/tests/differential/bool_english.ep b/tests/differential/bool_english.ep new file mode 100644 index 0000000..5e0963a --- /dev/null +++ b/tests/differential/bool_english.ep @@ -0,0 +1,12 @@ +define main: + set a to 5 + set b to 10 + if a < b and also b < 20: + display "both" + if a > b or else a < b: + display "one" + if not (a equals b): + display "different" + if a is not equal to b: + display "alias works" + return 0 diff --git a/tests/differential/break_continue.ep b/tests/differential/break_continue.ep new file mode 100644 index 0000000..f31eec5 --- /dev/null +++ b/tests/differential/break_continue.ep @@ -0,0 +1,11 @@ +define main: + set i to 0 + repeat while i < 10: + set i to i + 1 + if i % 2 == 0: + continue + if i > 7: + break + display i + display "done" + return 0 diff --git a/tests/differential/closure_counter.ep b/tests/differential/closure_counter.ep new file mode 100644 index 0000000..8b9e4b1 --- /dev/null +++ b/tests/differential/closure_counter.ep @@ -0,0 +1,14 @@ +define main: + set base to 100 + set add_base to given x: return x + base + display add_base(1) + display add_base(2) + set fns to create_list() + set i to 0 + repeat while i < 3: + set f to given y: return y * 10 + set ok to append_list(fns and f) + set i to i + 1 + set g to get_list(fns and 1) + display g(7) + return 0 diff --git a/tests/differential/closure_in_loop_block.ep b/tests/differential/closure_in_loop_block.ep new file mode 100644 index 0000000..d13e3bf --- /dev/null +++ b/tests/differential/closure_in_loop_block.ep @@ -0,0 +1,11 @@ +define main: + set fns to create_list() + set i to 0 + repeat while i < 3: + set g to given y: + return y * 10 + set ok to append_list(fns and g) + set i to i + 1 + set h to get_list(fns and 1) + display h(7) + return 0 diff --git a/tests/differential/closure_same_name.ep b/tests/differential/closure_same_name.ep new file mode 100644 index 0000000..6357b8d --- /dev/null +++ b/tests/differential/closure_same_name.ep @@ -0,0 +1,14 @@ +define first_maker returning Int: + set f to given x: + return x + 1 + return f(10) + +define second_maker returning Int: + set f to given x: + return x + 2 + return f(10) + +define main: + display first_maker() + display second_maker() + return 0 diff --git a/tests/differential/deep_recursion.ep b/tests/differential/deep_recursion.ep new file mode 100644 index 0000000..3305e3b --- /dev/null +++ b/tests/differential/deep_recursion.ep @@ -0,0 +1,8 @@ +define summer with n as Int returning Int: + if n < 1: + return 0 + return n + summer(n - 1) + +define main: + display summer(10000) + return 0 diff --git a/tests/differential/div_zero.ep b/tests/differential/div_zero.ep new file mode 100644 index 0000000..724b2b8 --- /dev/null +++ b/tests/differential/div_zero.ep @@ -0,0 +1,7 @@ +define main: + set a to 10 + set b to 0 + if a > 100: + set b to 1 + display a / b + return 0 diff --git a/tests/differential/enum_nofield.ep b/tests/differential/enum_nofield.ep new file mode 100644 index 0000000..2975e44 --- /dev/null +++ b/tests/differential/enum_nofield.ep @@ -0,0 +1,24 @@ +define choice Color: + variant Red + variant Green + variant Blue + +define name_of with c as Color returning Int: + check c: + if Red: + display "red" + return 1 + if Green: + display "green" + return 2 + if Blue: + display "blue" + return 3 + return 0 + +define main: + set r to Red + set g to Green + display name_of(r) + display name_of(g) + return 0 diff --git a/tests/differential/float_precision.ep b/tests/differential/float_precision.ep new file mode 100644 index 0000000..0e7df06 --- /dev/null +++ b/tests/differential/float_precision.ep @@ -0,0 +1,11 @@ +define main: + set a to 0.1 + set b to 0.2 + display a + b + set c to 1.0 + set d to 3.0 + display c / d + set e to 2.5 + set f to 2.5 + display e * f + return 0 diff --git a/tests/differential/for_each_mutate.ep b/tests/differential/for_each_mutate.ep new file mode 100644 index 0000000..a3ba940 --- /dev/null +++ b/tests/differential/for_each_mutate.ep @@ -0,0 +1,11 @@ +define main: + set xs to [1, 2, 3] + set total to 0 + for each x in xs: + set total to total + x + set ok to append_list(xs and x) + if length_list(xs) > 10: + break + display total + display length_list(xs) + return 0 diff --git a/tests/differential/fstring_edge.ep b/tests/differential/fstring_edge.ep new file mode 100644 index 0000000..4f431f3 --- /dev/null +++ b/tests/differential/fstring_edge.ep @@ -0,0 +1,8 @@ +define main: + set x to 3 + set y to 4 + display f"sum={x + y} prod={x * y}" + display f"nested {x} then {y} end" + set s to "abc" + display f"str:{s}!" + return 0 diff --git a/tests/differential/int_overflow.ep b/tests/differential/int_overflow.ep new file mode 100644 index 0000000..4ce3875 --- /dev/null +++ b/tests/differential/int_overflow.ep @@ -0,0 +1,6 @@ +define main: + set x to 4611686018427387904 + display x + x + set y to x * 4 + display y + return 0 diff --git a/tests/differential/list_oob.ep b/tests/differential/list_oob.ep new file mode 100644 index 0000000..b5e12ab --- /dev/null +++ b/tests/differential/list_oob.ep @@ -0,0 +1,5 @@ +define main: + set xs to [10, 20, 30] + display get_list(xs and 5) + display get_list(xs and 0 - 1) + return 0 diff --git a/tests/differential/map_stress.ep b/tests/differential/map_stress.ep new file mode 100644 index 0000000..651b059 --- /dev/null +++ b/tests/differential/map_stress.ep @@ -0,0 +1,17 @@ +define main: + set m to create_map() + set i to 0 + repeat while i < 5000: + set key to concat("k" and int_to_string(i)) + set ok to map_insert(m and key and i * 2) + set i to i + 1 + display map_size(m) + display map_get_val(m and "k4999") + display map_contains(m and "missing") + set j to 0 + repeat while j < 2500: + set dk to concat("k" and int_to_string(j)) + set ok2 to map_delete(m and dk) + set j to j + 1 + display map_size(m) + return 0 diff --git a/tests/differential/match_nested.ep b/tests/differential/match_nested.ep new file mode 100644 index 0000000..e70ab9d --- /dev/null +++ b/tests/differential/match_nested.ep @@ -0,0 +1,17 @@ +define choice Box: + variant Full with v as Int + variant Empty + +define main: + set a to Full with 5 + set b to Empty + check a: + if Full with x: + check b: + if Full with y: + display x + y + if Empty: + display x + if Empty: + display 0 + return 0 diff --git a/tests/differential/neg_arith.ep b/tests/differential/neg_arith.ep new file mode 100644 index 0000000..107d5af --- /dev/null +++ b/tests/differential/neg_arith.ep @@ -0,0 +1,10 @@ +define main: + set a to 0 - 7 + set b to 2 + display a / b + display a % b + set c to 7 + set d to 0 - 2 + display c / d + display c % d + return 0 diff --git a/tests/differential/nested_lists.ep b/tests/differential/nested_lists.ep new file mode 100644 index 0000000..f3ea2d1 --- /dev/null +++ b/tests/differential/nested_lists.ep @@ -0,0 +1,10 @@ +define main: + set inner to [1, 2] + set outer to create_list() + set ok to append_list(outer and inner) + set ok2 to append_list(outer and inner) + set got to get_list(outer and 0) + set ok3 to set_list(got and 0 and 99) + set other to get_list(outer and 1) + display get_list(other and 0) + return 0 diff --git a/tests/differential/not_bool.ep b/tests/differential/not_bool.ep new file mode 100644 index 0000000..badb906 --- /dev/null +++ b/tests/differential/not_bool.ep @@ -0,0 +1,8 @@ +define main: + set t to true + set f to false + if not f: + display "not-false" + if not t: + display "WRONG" + return 0 diff --git a/tests/differential/not_eq_int.ep b/tests/differential/not_eq_int.ep new file mode 100644 index 0000000..b7dec75 --- /dev/null +++ b/tests/differential/not_eq_int.ep @@ -0,0 +1,8 @@ +define main: + set a to 5 + set b to 5 + if not (a equals b): + display "WRONG" + else: + display "eq-correct" + return 0 diff --git a/tests/differential/not_eq_paren.ep b/tests/differential/not_eq_paren.ep new file mode 100644 index 0000000..0b29b56 --- /dev/null +++ b/tests/differential/not_eq_paren.ep @@ -0,0 +1,6 @@ +define main: + set a to 5 + set b to 10 + if not (a equals b): + display "ne-paren" + return 0 diff --git a/tests/differential/not_lt.ep b/tests/differential/not_lt.ep new file mode 100644 index 0000000..2051b96 --- /dev/null +++ b/tests/differential/not_lt.ep @@ -0,0 +1,8 @@ +define main: + set a to 5 + set b to 10 + if not (a > b): + display "not-gt" + if not (a < b): + display "WRONG" + return 0 diff --git a/tests/differential/not_str_eq.ep b/tests/differential/not_str_eq.ep new file mode 100644 index 0000000..2df2e87 --- /dev/null +++ b/tests/differential/not_str_eq.ep @@ -0,0 +1,6 @@ +define main: + set a to "x" + set b to "y" + if not (a equals b): + display "str-ne" + return 0 diff --git a/tests/differential/pop_empty.ep b/tests/differential/pop_empty.ep new file mode 100644 index 0000000..e7416e2 --- /dev/null +++ b/tests/differential/pop_empty.ep @@ -0,0 +1,5 @@ +define main: + set xs to create_list() + display pop_list(xs) + display length_list(xs) + return 0 diff --git a/tests/differential/shortcircuit.ep b/tests/differential/shortcircuit.ep new file mode 100644 index 0000000..539cdfa --- /dev/null +++ b/tests/differential/shortcircuit.ep @@ -0,0 +1,10 @@ +define noisy with tag as Int and val as Int returning Int: + display tag + return val + +define main: + if noisy(1 and 0) && noisy(2 and 1): + display 100 + if noisy(3 and 1) || noisy(4 and 1): + display 200 + return 0 diff --git a/tests/differential/str_empty.ep b/tests/differential/str_empty.ep new file mode 100644 index 0000000..d569dee --- /dev/null +++ b/tests/differential/str_empty.ep @@ -0,0 +1,9 @@ +define main: + display string_length("") + display concat("" and "") + display string_length(concat("" and "x")) + display substring("abc" and 1 and 0) + display string_length(substring("abc" and 0 and 2)) + set parts to string_split("" and ",") + display length_list(parts) + return 0 diff --git a/tests/differential/str_escapes.ep b/tests/differential/str_escapes.ep new file mode 100644 index 0000000..6dd288f --- /dev/null +++ b/tests/differential/str_escapes.ep @@ -0,0 +1,6 @@ +define main: + display "tab:\there" + display "line1\nline2" + display "quote:\"q\"" + display "backslash:\\" + return 0 diff --git a/tests/differential/str_unicode.ep b/tests/differential/str_unicode.ep new file mode 100644 index 0000000..1a93321 --- /dev/null +++ b/tests/differential/str_unicode.ep @@ -0,0 +1,6 @@ +define main: + set s to "héllo wörld 世界" + display s + display string_length(s) + display string_upper("straße") + return 0 diff --git a/tests/differential/string_compare.ep b/tests/differential/string_compare.ep new file mode 100644 index 0000000..55d7629 --- /dev/null +++ b/tests/differential/string_compare.ep @@ -0,0 +1,11 @@ +define main: + set a to concat("he" and "llo") + set b to concat("hel" and "lo") + if a equals b: + display "computed strings equal" + else: + display "BUG pointer compare" + set c to substring("xhellox" and 1 and 5) + if a equals c: + display "substring equal too" + return 0 diff --git a/tests/run_differential.sh b/tests/run_differential.sh new file mode 100755 index 0000000..c745609 --- /dev/null +++ b/tests/run_differential.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# run_differential.sh — differential testing between the two compilers. +# +# Every program in tests/differential/ is compiled by BOTH the Rust reference +# compiler (./target/release/ernos) and the self-hosted compiler (./epc); both +# binaries are run and their stdout + exit codes compared. The two compilers +# must agree exactly — on success AND on rejection. Any divergence is a bug in +# one of them (this suite found a `not (a > b)` miscompilation, a grammar +# drift on single-line closures, and three type-checker acceptance gaps). +# +# Exit code: 0 only if every program agrees (or is rejected by both). +set -uo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" +DIR="tests/differential" +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT + +if [ ! -x ./target/release/ernos ]; then echo "build the Rust compiler first (cargo build --release)"; exit 1; fi +if [ ! -x ./epc ]; then echo "build epc first (./target/release/ernos epc.ep)"; exit 1; fi + +pass=0; div=0; crash=0 +for f in "$DIR"/*.ep; do + name=$(basename "$f" .ep) + mkdir -p "$WORK/$name/r" "$WORK/$name/e" + cp "$f" "$WORK/$name/r/$name.ep"; cp "$f" "$WORK/$name/e/$name.ep" + (cd "$WORK/$name/r" && "$ROOT/target/release/ernos" "$name.ep" >compile.log 2>&1); rrc=$? + (cd "$WORK/$name/e" && "$ROOT/epc" "$name.ep" >compile.log 2>&1); erc=$? + if [ $rrc -ne 0 ] && [ $erc -ne 0 ]; then + echo "BOTH-REJECT $name"; pass=$((pass+1)); continue + fi + if [ $rrc -ne 0 ] || [ $erc -ne 0 ]; then + echo "COMPILE-DIVERGENCE $name: rust_rc=$rrc epc_rc=$erc"; div=$((div+1)); continue + fi + (cd "$WORK/$name/r" && perl -e 'alarm 20; exec @ARGV' "./$name" >run.out 2>run.err); rrun=$? + (cd "$WORK/$name/e" && perl -e 'alarm 20; exec @ARGV' "./$name" >run.out 2>run.err); erun=$? + if [ $rrun -ge 128 ] || [ $erun -ge 128 ]; then + echo "CRASH $name: rust_run=$rrun epc_run=$erun"; crash=$((crash+1)); continue + fi + if [ $rrun -ne $erun ] || ! diff -q "$WORK/$name/r/run.out" "$WORK/$name/e/run.out" >/dev/null; then + echo "RUN-DIVERGENCE $name: rust_rc=$rrun epc_rc=$erun" + diff "$WORK/$name/r/run.out" "$WORK/$name/e/run.out" | head -6 | sed 's/^/ /' + div=$((div+1)); continue + fi + echo "AGREE $name"; pass=$((pass+1)) +done +total=$((pass+div+crash)) +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " differential: $pass/$total agree, $div divergence, $crash crash" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +[ $div -eq 0 ] && [ $crash -eq 0 ] From 9c07d3ba00a46584b28ed9f4f08790078363fc06 Mon Sep 17 00:00:00 2001 From: MettaMazza Date: Fri, 3 Jul 2026 09:46:05 +0100 Subject: [PATCH 51/51] feat: MIT license + flagship demo (The Plainville Bakery) + float f-string fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LICENSE: MIT — free for anyone, for any purpose. examples/bakery.ep: a ~300-line day-in-the-life simulation exercising the whole language in plain English — structs + methods, traits, enums + check, try/Result, closures (single-line and block), a custom Iterator, async ovens on the coroutine event loop, spawned threads + channels, floats, f-strings, maps, lists, plain-English insertion sort, break/continue, recursion at C speed, file I/O, and SHA-256 receipt stamps. Both compilers produce byte-identical output for it; CI now proves that on every push. Writing the flagship surfaced a real bug, fixed in BOTH compilers: an f-string interpolating a Float printed the double's raw bit pattern as a huge integer (ep_auto_to_string cannot know the bits are a double). New shared- runtime ep_float_to_string (%.15g, matching display) and both codegens route Float-typed interpolations to it. Regression test added to the differential suite (now 38 programs). Gates: Rust suite 71/71, parity 53/53 + 12/12 rejections, differential 38/38, fixpoint byte-identical, clang-only bootstrap verify green (re-frozen here), zero warnings. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 18 ++ .gitignore | 2 + LICENSE | 21 ++ README.md | 28 +++ bootstrap/epc_bootstrap.c | 110 ++++++---- ep_codegen.ep | 14 +- ep_runtime_gen.ep | 60 +++--- examples/bakery.ep | 300 ++++++++++++++++++++++++++++ runtime/ep_runtime.c | 14 ++ src/codegen.rs | 11 + src/type_check.rs | 1 + tests/differential/fstring_float.ep | 7 + 12 files changed, 528 insertions(+), 58 deletions(-) create mode 100644 LICENSE create mode 100644 examples/bakery.ep create mode 100644 tests/differential/fstring_float.ep diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9689bce..ef94aab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,15 @@ jobs: - name: Clang-only bootstrap (no Rust) — build, fixpoint, freshness run: bash bootstrap/verify.sh + - name: Flagship demo — both compilers, identical output + run: | + ./target/release/ernos examples/bakery.ep + ./examples/bakery > /tmp/bakery_rust.out + ./epc examples/bakery.ep + ./examples/bakery > /tmp/bakery_epc.out + diff /tmp/bakery_rust.out /tmp/bakery_epc.out + echo "flagship output identical across compilers" + - name: Run conformance tests run: | for f in conformance/test_*.ep; do @@ -96,6 +105,15 @@ jobs: - name: Clang-only bootstrap (no Rust) — build, fixpoint, freshness run: bash bootstrap/verify.sh + - name: Flagship demo — both compilers, identical output + run: | + ./target/release/ernos examples/bakery.ep + ./examples/bakery > /tmp/bakery_rust.out + ./epc examples/bakery.ep + ./examples/bakery > /tmp/bakery_epc.out + diff /tmp/bakery_rust.out /tmp/bakery_epc.out + echo "flagship output identical across compilers" + - name: Run conformance tests run: | for f in conformance/test_*.ep; do diff --git a/.gitignore b/.gitignore index 6b72b1f..8f1db23 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,5 @@ tests/* # Debug bundles under tests/ (the !tests/*/ negation would otherwise re-include them) tests/**/*.dSYM/ /epc_stage1 +bakery_report.txt +examples/bakery diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..64406bb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 MettaMazza + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 17c91f7..93ed806 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@

Version + License: MIT Rust suite 71/71 Self-hosted parity 53/53 Rejection gate 12/12 @@ -44,6 +45,27 @@ Ernos ships **two** complete compilers for the same language: > `clang bootstrap/epc_bootstrap.c -o epc && ./epc epc.ep` → a working compiler that recompiles itself and passes the full suite. +### See the whole language in one program + +[`examples/bakery.ep`](examples/bakery.ep) — **The Plainville Bakery** — is a day-in-the-life simulation that exercises every major feature in ~300 lines of plain English: structs + methods, traits, enums + pattern matching, `try`/Result error handling, closures, a custom iterator, async ovens on the event loop, spawned worker threads with channels, floats, f-strings, maps, file I/O, SHA-256 receipts, and a plain-English insertion sort. + +```bash +./target/release/ernos examples/bakery.ep && ./examples/bakery # reference compiler +./epc examples/bakery.ep && ./examples/bakery # self-hosted compiler +``` + +Both compilers produce **byte-identical output** for it — CI proves that on every push. + +```ernos +define sell on Pastry with qty as Int returning Outcome: + if qty < 1: + return Refused with "you have to buy at least one" + if qty > self.stock: + return Refused with "not enough in the case" + set self.stock to self.stock - qty + return Sold with self.price_cents * qty +``` + --- ## Why Ernos? @@ -475,6 +497,12 @@ cp -R ernosplain-syntax ~/.vscode/extensions/ --- +## License + +MIT — free for anyone, for any purpose. See [LICENSE](LICENSE). + +--- +

Ernos — Code that reads like English. Runs like C.

diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c index 8cce170..b59916d 100644 --- a/bootstrap/epc_bootstrap.c +++ b/bootstrap/epc_bootstrap.c @@ -1776,6 +1776,7 @@ long long channel_try_recv(long long chan_ptr, long long out_ptr); long long channel_has_data(long long chan_ptr); long long channel_select(long long channels_list, long long timeout_ms); long long ep_auto_to_string(long long val); +long long ep_float_to_string(long long bits); typedef struct EpChannel_ { long long* data; @@ -4497,6 +4498,19 @@ long long ep_auto_to_string(long long val) { return (long long)buf; } +/* Format a Float (double bits carried in a long long) as a string. F-string + interpolation routes Float-typed expressions here: ep_auto_to_string cannot + know the bits are a double and would print them as a huge integer. Uses the + same %.15g format as `display` so a float reads identically both ways. */ +long long ep_float_to_string(long long bits) { + double d; + memcpy(&d, &bits, sizeof(double)); + char* buf = (char*)malloc(40); + snprintf(buf, 40, "%.15g", d); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + long long ep_random_int(long long min, long long max) { if (max <= min) return min; /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */ @@ -11648,6 +11662,7 @@ long long analyze_return_types(long long state, long long program) { ok = map_put(keys, values, (long long)"ep_sleep_ms", 1LL); ok = map_put(keys, values, (long long)"concat", 3LL); ok = map_put(keys, values, (long long)"ep_auto_to_string", 3LL); + ok = map_put(keys, values, (long long)"ep_float_to_string", 3LL); ok = map_put(keys, values, (long long)"int_to_string", 3LL); ok = map_put(keys, values, (long long)"ep_int_to_str", 3LL); ok = map_put(keys, values, (long long)"string_upper", 3LL); @@ -13194,6 +13209,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon long long is_string = 0; long long cmp_op = 0; long long args = 0; + long long fexpr = 0; long long args_len = 0; long long formatted_args = 0; long long idx = 0; @@ -13310,6 +13326,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon ep_gc_push_root(&lu); ep_gc_push_root(&ru); ep_gc_push_root(&fr2); + ep_gc_push_root(&fexpr); ep_gc_push_root(&formatted_args); ep_gc_push_root(&arg_val); ep_gc_push_root(&casted); @@ -13560,6 +13577,17 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon if (type == 6LL) { name = get_list(expr, 1LL); args = get_list(expr, 2LL); + if ((strcmp((char*)(long long)"ep_auto_to_string", (char*)name) == 0)) { + if (length_list(args) == 1LL) { + if (infer_type(state, get_list(args, 0LL), var_keys, var_values) == 8LL) { + fexpr = gen_expr(state, get_list(args, 0LL), var_keys, var_values); + fres = string_concat((long long)"ep_float_to_string(", fexpr); + fres = string_concat(fres, (long long)")"); + ret_val = fres; + goto L_cleanup; + } + } + } args_len = length_list(args); formatted_args = create_list(); idx = 0LL; @@ -14018,7 +14046,7 @@ long long gen_expr(long long state, long long expr, long long var_keys, long lon ret_val = (long long)""; goto L_cleanup; L_cleanup: - ep_gc_pop_roots(38); + ep_gc_pop_roots(39); return ret_val; } @@ -17504,6 +17532,7 @@ long long ep_rt_core_11() { ok = append_list(lines, (long long)"long long channel_has_data(long long chan_ptr);\n"); ok = append_list(lines, (long long)"long long channel_select(long long channels_list, long long timeout_ms);\n"); ok = append_list(lines, (long long)"long long ep_auto_to_string(long long val);\n"); + ok = append_list(lines, (long long)"long long ep_float_to_string(long long bits);\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"typedef struct EpChannel_ {\n"); ok = append_list(lines, (long long)" long long* data;\n"); @@ -17525,7 +17554,6 @@ long long ep_rt_core_11() { ok = append_list(lines, (long long)"static int ep_channel_count = 0;\n"); ok = append_list(lines, (long long)"static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n"); ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"static void ep_register_channel(EpChannel* chan) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17542,6 +17570,7 @@ long long ep_rt_core_12() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)"static void ep_register_channel(EpChannel* chan) {\n"); ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n"); ok = append_list(lines, (long long)" if (ep_channel_count < EP_MAX_CHANNELS) {\n"); ok = append_list(lines, (long long)" ep_channel_registry[ep_channel_count++] = chan;\n"); @@ -17691,7 +17720,6 @@ long long ep_rt_core_12() { ok = append_list(lines, (long long)" while (1) {\n"); ok = append_list(lines, (long long)" // Poll all channels\n"); ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n"); - ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)list->data[i];\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17708,6 +17736,7 @@ long long ep_rt_core_13() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)list->data[i];\n"); ok = append_list(lines, (long long)" if (chan) {\n"); ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n"); ok = append_list(lines, (long long)" if (chan->size > 0) {\n"); @@ -17857,7 +17886,6 @@ long long ep_rt_core_13() { ok = append_list(lines, (long long)" int opt = 1;\n"); ok = append_list(lines, (long long)" setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n"); ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n"); - ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -17874,6 +17902,7 @@ long long ep_rt_core_14() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n"); ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n"); ok = append_list(lines, (long long)" serv_addr.sin_addr.s_addr = INADDR_ANY;\n"); ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n"); @@ -18023,7 +18052,6 @@ long long ep_rt_core_14() { ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long ep_dlcall0(long long fptr) {\n"); ok = append_list(lines, (long long)" return ((ep_fn0)fptr)();\n"); - ok = append_list(lines, (long long)"}\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18040,6 +18068,7 @@ long long ep_rt_core_15() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"long long ep_dlcall1(long long fptr, long long a0) {\n"); ok = append_list(lines, (long long)" return ((ep_fn1)fptr)(a0);\n"); ok = append_list(lines, (long long)"}\n"); @@ -18189,7 +18218,6 @@ long long ep_rt_core_15() { ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)map->entries[i].value);\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\n"); - ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)map->entries[i].key);\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18206,6 +18234,7 @@ long long ep_rt_core_16() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)map->entries[i].key);\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)"}\n"); @@ -18355,7 +18384,6 @@ long long ep_rt_core_16() { ok = append_list(lines, (long long)" while (map->entries[next_h].used) {\n"); ok = append_list(lines, (long long)" char* k = map->entries[next_h].key;\n"); ok = append_list(lines, (long long)" long long v = map->entries[next_h].value;\n"); - ok = append_list(lines, (long long)" map->entries[next_h].key = NULL;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18372,6 +18400,7 @@ long long ep_rt_core_17() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" map->entries[next_h].key = NULL;\n"); ok = append_list(lines, (long long)" map->entries[next_h].value = 0;\n"); ok = append_list(lines, (long long)" map->entries[next_h].used = 0;\n"); ok = append_list(lines, (long long)" map->size--;\n"); @@ -18521,7 +18550,6 @@ long long ep_rt_core_17() { ok = append_list(lines, (long long)" if (!dq) return 0;\n"); ok = append_list(lines, (long long)" free(dq->data);\n"); ok = append_list(lines, (long long)" free(dq);\n"); - ok = append_list(lines, (long long)" return 0;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18538,6 +18566,7 @@ long long ep_rt_core_18() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" return 0;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* Filesystem Operations */\n"); @@ -18687,7 +18716,6 @@ long long ep_rt_core_18() { ok = append_list(lines, (long long)" \"%s %s HTTP/1.1\\r\\n\"\n"); ok = append_list(lines, (long long)" \"Host: %s\\r\\n\"\n"); ok = append_list(lines, (long long)" \"Content-Length: %zu\\r\\n\"\n"); - ok = append_list(lines, (long long)" \"Connection: close\\r\\n\"\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18704,6 +18732,7 @@ long long ep_rt_core_19() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" \"Connection: close\\r\\n\"\n"); ok = append_list(lines, (long long)" \"%s%s\"\n"); ok = append_list(lines, (long long)" \"\\r\\n\",\n"); ok = append_list(lines, (long long)" method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n"); @@ -18853,7 +18882,6 @@ long long ep_rt_core_19() { ok = append_list(lines, (long long)" ep_sha256_final(&ctx, hash);\n"); ok = append_list(lines, (long long)" char* result = malloc(65);\n"); ok = append_list(lines, (long long)" if (result) {\n"); - ok = append_list(lines, (long long)" for (int i = 0; i < 32; i++) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -18870,6 +18898,7 @@ long long ep_rt_core_20() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" for (int i = 0; i < 32; i++) {\n"); ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n"); ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" result[64] = '\\0';\n"); @@ -19019,7 +19048,6 @@ long long ep_rt_core_20() { ok = append_list(lines, (long long)"void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n"); ok = append_list(lines, (long long)" unsigned char bits[8];\n"); ok = append_list(lines, (long long)" bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n"); - ok = append_list(lines, (long long)" bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19036,6 +19064,7 @@ long long ep_rt_core_21() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n"); ok = append_list(lines, (long long)" unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n"); ok = append_list(lines, (long long)" unsigned char padding[64];\n"); ok = append_list(lines, (long long)" memset(padding, 0, 64); padding[0] = 0x80;\n"); @@ -19185,7 +19214,6 @@ long long ep_rt_core_21() { ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long sqlite_get_callback_ptr(long long dummy) {\n"); ok = append_list(lines, (long long)" return (long long)sqlite_list_callback;\n"); - ok = append_list(lines, (long long)"}\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19202,6 +19230,7 @@ long long ep_rt_core_22() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"/* SQLite type-safe wrappers — marshal between int and long long */\n"); ok = append_list(lines, (long long)"#ifdef EP_HAS_SQLITE\n"); @@ -19351,7 +19380,6 @@ long long ep_rt_core_22() { ok = append_list(lines, (long long)" char* sub = malloc(len + 1);\n"); ok = append_list(lines, (long long)" if (!sub) {\n"); ok = append_list(lines, (long long)" char* empty = malloc(1);\n"); - ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19368,6 +19396,7 @@ long long ep_rt_core_23() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n"); ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n"); ok = append_list(lines, (long long)" return empty;\n"); ok = append_list(lines, (long long)" }\n"); @@ -19517,7 +19546,6 @@ long long ep_rt_core_23() { ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n"); ok = append_list(lines, (long long)" const char* content = (const char*)content_ptr;\n"); ok = append_list(lines, (long long)" FILE* f = fopen(path, \"ab\");\n"); - ok = append_list(lines, (long long)" if (!f) return 0;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19534,6 +19562,7 @@ long long ep_rt_core_24() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" if (!f) return 0;\n"); ok = append_list(lines, (long long)" fputs(content, f);\n"); ok = append_list(lines, (long long)" fclose(f);\n"); ok = append_list(lines, (long long)" return 1;\n"); @@ -19683,7 +19712,6 @@ long long ep_rt_core_24() { ok = append_list(lines, (long long)" const char* val = getenv((const char*)name_ptr);\n"); ok = append_list(lines, (long long)" return val ? (long long)val : (long long)\"\";\n"); ok = append_list(lines, (long long)"}\n"); - ok = append_list(lines, (long long)"\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19700,6 +19728,7 @@ long long ep_rt_core_25() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)"long long ep_setenv(long long name_ptr, long long val_ptr) {\n"); ok = append_list(lines, (long long)" return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n"); ok = append_list(lines, (long long)"}\n"); @@ -19849,7 +19878,6 @@ long long ep_rt_core_25() { ok = append_list(lines, (long long)"long long ep_rwlock_create(void) {\n"); ok = append_list(lines, (long long)" SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n"); ok = append_list(lines, (long long)" InitializeSRWLock(rwl);\n"); - ok = append_list(lines, (long long)" return (long long)rwl;\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -19866,6 +19894,7 @@ long long ep_rt_core_26() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" return (long long)rwl;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n"); ok = append_list(lines, (long long)" AcquireSRWLockShared((SRWLOCK*)rwl);\n"); @@ -20015,7 +20044,6 @@ long long ep_rt_core_26() { ok = append_list(lines, (long long)" return 0;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"/* Semaphore via mutex+condvar (portable) */\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20032,6 +20060,7 @@ long long ep_rt_core_27() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)"/* Semaphore via mutex+condvar (portable) */\n"); ok = append_list(lines, (long long)"typedef struct {\n"); ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n"); ok = append_list(lines, (long long)" pthread_cond_t cond;\n"); @@ -20181,7 +20210,6 @@ long long ep_rt_core_27() { ok = append_list(lines, (long long)" memcpy(result, text, match.rm_so);\n"); ok = append_list(lines, (long long)" memcpy(result + match.rm_so, repl, rlen);\n"); ok = append_list(lines, (long long)" memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\n"); - ok = append_list(lines, (long long)" result[new_len] = '\\0';\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20198,6 +20226,7 @@ long long ep_rt_core_28() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" result[new_len] = '\\0';\n"); ok = append_list(lines, (long long)" regfree(®ex);\n"); ok = append_list(lines, (long long)" return (long long)result;\n"); ok = append_list(lines, (long long)"}\n"); @@ -20347,7 +20376,6 @@ long long ep_rt_core_28() { ok = append_list(lines, (long long)" if (!result) return (long long)strdup(s);\n"); ok = append_list(lines, (long long)" char* dst = result;\n"); ok = append_list(lines, (long long)" p = s;\n"); - ok = append_list(lines, (long long)" while (*p) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20364,6 +20392,7 @@ long long ep_rt_core_29() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" while (*p) {\n"); ok = append_list(lines, (long long)" if (strncmp(p, old_str, old_len) == 0) {\n"); ok = append_list(lines, (long long)" memcpy(dst, new_str, new_len);\n"); ok = append_list(lines, (long long)" dst += new_len;\n"); @@ -20513,7 +20542,6 @@ long long ep_rt_core_29() { ok = append_list(lines, (long long)" return (long long)buf;\n"); ok = append_list(lines, (long long)"}\n"); ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)"long long ep_random_int(long long min, long long max) {\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20530,6 +20558,20 @@ long long ep_rt_core_30() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)"/* Format a Float (double bits carried in a long long) as a string. F-string\n"); + ok = append_list(lines, (long long)" interpolation routes Float-typed expressions here: ep_auto_to_string cannot\n"); + ok = append_list(lines, (long long)" know the bits are a double and would print them as a huge integer. Uses the\n"); + ok = append_list(lines, (long long)" same %.15g format as `display` so a float reads identically both ways. */\n"); + ok = append_list(lines, (long long)"long long ep_float_to_string(long long bits) {\n"); + ok = append_list(lines, (long long)" double d;\n"); + ok = append_list(lines, (long long)" memcpy(&d, &bits, sizeof(double));\n"); + ok = append_list(lines, (long long)" char* buf = (char*)malloc(40);\n"); + ok = append_list(lines, (long long)" snprintf(buf, 40, \"%.15g\", d);\n"); + ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n"); + ok = append_list(lines, (long long)" return (long long)buf;\n"); + ok = append_list(lines, (long long)"}\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)"long long ep_random_int(long long min, long long max) {\n"); ok = append_list(lines, (long long)" if (max <= min) return min;\n"); ok = append_list(lines, (long long)" /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n"); ok = append_list(lines, (long long)" unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n"); @@ -20666,20 +20708,6 @@ long long ep_rt_core_30() { ok = append_list(lines, (long long)" size_t len = strlen((const char*)data);\n"); ok = append_list(lines, (long long)"\n"); ok = append_list(lines, (long long)" unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;\n"); - ok = append_list(lines, (long long)" size_t new_len = len + 1;\n"); - ok = append_list(lines, (long long)" while (new_len % 64 != 56) new_len++;\n"); - ok = append_list(lines, (long long)" unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);\n"); - ok = append_list(lines, (long long)" memcpy(msg, data, len);\n"); - ok = append_list(lines, (long long)" msg[len] = 0x80;\n"); - ok = append_list(lines, (long long)" unsigned long long bits_len = (unsigned long long)len * 8;\n"); - ok = append_list(lines, (long long)" for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));\n"); - ok = append_list(lines, (long long)"\n"); - ok = append_list(lines, (long long)" for (size_t offset = 0; offset < new_len + 8; offset += 64) {\n"); - ok = append_list(lines, (long long)" unsigned int w[80];\n"); - ok = append_list(lines, (long long)" for (int i = 0; i < 16; i++) {\n"); - ok = append_list(lines, (long long)" w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n"); - ok = append_list(lines, (long long)" ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n"); - ok = append_list(lines, (long long)" }\n"); ret_val = join_strings(lines); goto L_cleanup; L_cleanup: @@ -20696,6 +20724,20 @@ long long ep_rt_core_31() { ep_gc_maybe_collect(); lines = create_list(); + ok = append_list(lines, (long long)" size_t new_len = len + 1;\n"); + ok = append_list(lines, (long long)" while (new_len % 64 != 56) new_len++;\n"); + ok = append_list(lines, (long long)" unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);\n"); + ok = append_list(lines, (long long)" memcpy(msg, data, len);\n"); + ok = append_list(lines, (long long)" msg[len] = 0x80;\n"); + ok = append_list(lines, (long long)" unsigned long long bits_len = (unsigned long long)len * 8;\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));\n"); + ok = append_list(lines, (long long)"\n"); + ok = append_list(lines, (long long)" for (size_t offset = 0; offset < new_len + 8; offset += 64) {\n"); + ok = append_list(lines, (long long)" unsigned int w[80];\n"); + ok = append_list(lines, (long long)" for (int i = 0; i < 16; i++) {\n"); + ok = append_list(lines, (long long)" w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n"); + ok = append_list(lines, (long long)" ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n"); + ok = append_list(lines, (long long)" }\n"); ok = append_list(lines, (long long)" for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n"); ok = append_list(lines, (long long)" unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n"); ok = append_list(lines, (long long)" for (int i = 0; i < 80; i++) {\n"); diff --git a/ep_codegen.ep b/ep_codegen.ep index f84fa55..951d898 100644 --- a/ep_codegen.ep +++ b/ep_codegen.ep @@ -601,6 +601,7 @@ define analyze_return_types with state and program: # String builtins from the shared runtime (TYPE_DYNSTR = 3 unless noted) set ok to map_put(keys and values and "concat" and 3) set ok to map_put(keys and values and "ep_auto_to_string" and 3) + set ok to map_put(keys and values and "ep_float_to_string" and 3) set ok to map_put(keys and values and "int_to_string" and 3) set ok to map_put(keys and values and "ep_int_to_str" and 3) set ok to map_put(keys and values and "string_upper" and 3) @@ -1888,7 +1889,18 @@ define gen_expr with state and expr and var_keys and var_values: if type == 6: # NODE_CALL set name to get_list(expr and 1) set args to get_list(expr and 2) - + + # F-string interpolation of a Float: ep_auto_to_string would print the + # double's raw bits as an integer, so route to the float formatter + # (same %.15g as display). Mirrors the reference compiler. + if "ep_auto_to_string" equals name: + if length_list(args) == 1: + if infer_type(state and get_list(args and 0) and var_keys and var_values) == 8: + set fexpr to gen_expr(state and get_list(args and 0) and var_keys and var_values) + set fres to string_concat("ep_float_to_string(" and fexpr) + set fres to string_concat(fres and ")") + return fres + set args_len to length_list(args) set formatted_args to create_list() diff --git a/ep_runtime_gen.ep b/ep_runtime_gen.ep index 0099cdc..a8b97de 100644 --- a/ep_runtime_gen.ep +++ b/ep_runtime_gen.ep @@ -1826,6 +1826,7 @@ define ep_rt_core_11: set ok to append_list(lines and "long long channel_has_data(long long chan_ptr);\n") set ok to append_list(lines and "long long channel_select(long long channels_list, long long timeout_ms);\n") set ok to append_list(lines and "long long ep_auto_to_string(long long val);\n") + set ok to append_list(lines and "long long ep_float_to_string(long long bits);\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "typedef struct EpChannel_ {\n") set ok to append_list(lines and " long long* data;\n") @@ -1847,11 +1848,11 @@ define ep_rt_core_11: set ok to append_list(lines and "static int ep_channel_count = 0;\n") set ok to append_list(lines and "static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n") set ok to append_list(lines and "\n") - set ok to append_list(lines and "static void ep_register_channel(EpChannel* chan) {\n") return join_strings(lines) define ep_rt_core_12: set lines to create_list() + set ok to append_list(lines and "static void ep_register_channel(EpChannel* chan) {\n") set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n") set ok to append_list(lines and " if (ep_channel_count < EP_MAX_CHANNELS) {\n") set ok to append_list(lines and " ep_channel_registry[ep_channel_count++] = chan;\n") @@ -2001,11 +2002,11 @@ define ep_rt_core_12: set ok to append_list(lines and " while (1) {\n") set ok to append_list(lines and " // Poll all channels\n") set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n") - set ok to append_list(lines and " EpChannel* chan = (EpChannel*)list->data[i];\n") return join_strings(lines) define ep_rt_core_13: set lines to create_list() + set ok to append_list(lines and " EpChannel* chan = (EpChannel*)list->data[i];\n") set ok to append_list(lines and " if (chan) {\n") set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n") set ok to append_list(lines and " if (chan->size > 0) {\n") @@ -2155,11 +2156,11 @@ define ep_rt_core_13: set ok to append_list(lines and " int opt = 1;\n") set ok to append_list(lines and " setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n") set ok to append_list(lines and " struct sockaddr_in serv_addr;\n") - set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") return join_strings(lines) define ep_rt_core_14: set lines to create_list() + set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n") set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n") set ok to append_list(lines and " serv_addr.sin_addr.s_addr = INADDR_ANY;\n") set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n") @@ -2309,11 +2310,11 @@ define ep_rt_core_14: set ok to append_list(lines and "\n") set ok to append_list(lines and "long long ep_dlcall0(long long fptr) {\n") set ok to append_list(lines and " return ((ep_fn0)fptr)();\n") - set ok to append_list(lines and "}\n") return join_strings(lines) define ep_rt_core_15: set lines to create_list() + set ok to append_list(lines and "}\n") set ok to append_list(lines and "long long ep_dlcall1(long long fptr, long long a0) {\n") set ok to append_list(lines and " return ((ep_fn1)fptr)(a0);\n") set ok to append_list(lines and "}\n") @@ -2463,11 +2464,11 @@ define ep_rt_core_15: set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].value);\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n") - set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].key);\n") return join_strings(lines) define ep_rt_core_16: set lines to create_list() + set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].key);\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and "}\n") @@ -2617,11 +2618,11 @@ define ep_rt_core_16: set ok to append_list(lines and " while (map->entries[next_h].used) {\n") set ok to append_list(lines and " char* k = map->entries[next_h].key;\n") set ok to append_list(lines and " long long v = map->entries[next_h].value;\n") - set ok to append_list(lines and " map->entries[next_h].key = NULL;\n") return join_strings(lines) define ep_rt_core_17: set lines to create_list() + set ok to append_list(lines and " map->entries[next_h].key = NULL;\n") set ok to append_list(lines and " map->entries[next_h].value = 0;\n") set ok to append_list(lines and " map->entries[next_h].used = 0;\n") set ok to append_list(lines and " map->size--;\n") @@ -2771,11 +2772,11 @@ define ep_rt_core_17: set ok to append_list(lines and " if (!dq) return 0;\n") set ok to append_list(lines and " free(dq->data);\n") set ok to append_list(lines and " free(dq);\n") - set ok to append_list(lines and " return 0;\n") return join_strings(lines) define ep_rt_core_18: set lines to create_list() + set ok to append_list(lines and " return 0;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "/* Filesystem Operations */\n") @@ -2925,11 +2926,11 @@ define ep_rt_core_18: set ok to append_list(lines and " \"%s %s HTTP/1.1\\r\\n\"\n") set ok to append_list(lines and " \"Host: %s\\r\\n\"\n") set ok to append_list(lines and " \"Content-Length: %zu\\r\\n\"\n") - set ok to append_list(lines and " \"Connection: close\\r\\n\"\n") return join_strings(lines) define ep_rt_core_19: set lines to create_list() + set ok to append_list(lines and " \"Connection: close\\r\\n\"\n") set ok to append_list(lines and " \"%s%s\"\n") set ok to append_list(lines and " \"\\r\\n\",\n") set ok to append_list(lines and " method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n") @@ -3079,11 +3080,11 @@ define ep_rt_core_19: set ok to append_list(lines and " ep_sha256_final(&ctx, hash);\n") set ok to append_list(lines and " char* result = malloc(65);\n") set ok to append_list(lines and " if (result) {\n") - set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n") return join_strings(lines) define ep_rt_core_20: set lines to create_list() + set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n") set ok to append_list(lines and " snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n") set ok to append_list(lines and " }\n") set ok to append_list(lines and " result[64] = '\\0';\n") @@ -3233,11 +3234,11 @@ define ep_rt_core_20: set ok to append_list(lines and "void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n") set ok to append_list(lines and " unsigned char bits[8];\n") set ok to append_list(lines and " bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n") - set ok to append_list(lines and " bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n") return join_strings(lines) define ep_rt_core_21: set lines to create_list() + set ok to append_list(lines and " bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n") set ok to append_list(lines and " unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n") set ok to append_list(lines and " unsigned char padding[64];\n") set ok to append_list(lines and " memset(padding, 0, 64); padding[0] = 0x80;\n") @@ -3387,11 +3388,11 @@ define ep_rt_core_21: set ok to append_list(lines and "\n") set ok to append_list(lines and "long long sqlite_get_callback_ptr(long long dummy) {\n") set ok to append_list(lines and " return (long long)sqlite_list_callback;\n") - set ok to append_list(lines and "}\n") return join_strings(lines) define ep_rt_core_22: set lines to create_list() + set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") set ok to append_list(lines and "/* SQLite type-safe wrappers — marshal between int and long long */\n") set ok to append_list(lines and "#ifdef EP_HAS_SQLITE\n") @@ -3541,11 +3542,11 @@ define ep_rt_core_22: set ok to append_list(lines and " char* sub = malloc(len + 1);\n") set ok to append_list(lines and " if (!sub) {\n") set ok to append_list(lines and " char* empty = malloc(1);\n") - set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") return join_strings(lines) define ep_rt_core_23: set lines to create_list() + set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n") set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n") set ok to append_list(lines and " return empty;\n") set ok to append_list(lines and " }\n") @@ -3695,11 +3696,11 @@ define ep_rt_core_23: set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n") set ok to append_list(lines and " const char* content = (const char*)content_ptr;\n") set ok to append_list(lines and " FILE* f = fopen(path, \"ab\");\n") - set ok to append_list(lines and " if (!f) return 0;\n") return join_strings(lines) define ep_rt_core_24: set lines to create_list() + set ok to append_list(lines and " if (!f) return 0;\n") set ok to append_list(lines and " fputs(content, f);\n") set ok to append_list(lines and " fclose(f);\n") set ok to append_list(lines and " return 1;\n") @@ -3849,11 +3850,11 @@ define ep_rt_core_24: set ok to append_list(lines and " const char* val = getenv((const char*)name_ptr);\n") set ok to append_list(lines and " return val ? (long long)val : (long long)\"\";\n") set ok to append_list(lines and "}\n") - set ok to append_list(lines and "\n") return join_strings(lines) define ep_rt_core_25: set lines to create_list() + set ok to append_list(lines and "\n") set ok to append_list(lines and "long long ep_setenv(long long name_ptr, long long val_ptr) {\n") set ok to append_list(lines and " return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;\n") set ok to append_list(lines and "}\n") @@ -4003,11 +4004,11 @@ define ep_rt_core_25: set ok to append_list(lines and "long long ep_rwlock_create(void) {\n") set ok to append_list(lines and " SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n") set ok to append_list(lines and " InitializeSRWLock(rwl);\n") - set ok to append_list(lines and " return (long long)rwl;\n") return join_strings(lines) define ep_rt_core_26: set lines to create_list() + set ok to append_list(lines and " return (long long)rwl;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "long long ep_rwlock_read_lock(long long rwl) {\n") set ok to append_list(lines and " AcquireSRWLockShared((SRWLOCK*)rwl);\n") @@ -4157,11 +4158,11 @@ define ep_rt_core_26: set ok to append_list(lines and " return 0;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - set ok to append_list(lines and "/* Semaphore via mutex+condvar (portable) */\n") return join_strings(lines) define ep_rt_core_27: set lines to create_list() + set ok to append_list(lines and "/* Semaphore via mutex+condvar (portable) */\n") set ok to append_list(lines and "typedef struct {\n") set ok to append_list(lines and " pthread_mutex_t mutex;\n") set ok to append_list(lines and " pthread_cond_t cond;\n") @@ -4311,11 +4312,11 @@ define ep_rt_core_27: set ok to append_list(lines and " memcpy(result, text, match.rm_so);\n") set ok to append_list(lines and " memcpy(result + match.rm_so, repl, rlen);\n") set ok to append_list(lines and " memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\n") - set ok to append_list(lines and " result[new_len] = '\\0';\n") return join_strings(lines) define ep_rt_core_28: set lines to create_list() + set ok to append_list(lines and " result[new_len] = '\\0';\n") set ok to append_list(lines and " regfree(®ex);\n") set ok to append_list(lines and " return (long long)result;\n") set ok to append_list(lines and "}\n") @@ -4465,11 +4466,11 @@ define ep_rt_core_28: set ok to append_list(lines and " if (!result) return (long long)strdup(s);\n") set ok to append_list(lines and " char* dst = result;\n") set ok to append_list(lines and " p = s;\n") - set ok to append_list(lines and " while (*p) {\n") return join_strings(lines) define ep_rt_core_29: set lines to create_list() + set ok to append_list(lines and " while (*p) {\n") set ok to append_list(lines and " if (strncmp(p, old_str, old_len) == 0) {\n") set ok to append_list(lines and " memcpy(dst, new_str, new_len);\n") set ok to append_list(lines and " dst += new_len;\n") @@ -4619,11 +4620,24 @@ define ep_rt_core_29: set ok to append_list(lines and " return (long long)buf;\n") set ok to append_list(lines and "}\n") set ok to append_list(lines and "\n") - set ok to append_list(lines and "long long ep_random_int(long long min, long long max) {\n") return join_strings(lines) define ep_rt_core_30: set lines to create_list() + set ok to append_list(lines and "/* Format a Float (double bits carried in a long long) as a string. F-string\n") + set ok to append_list(lines and " interpolation routes Float-typed expressions here: ep_auto_to_string cannot\n") + set ok to append_list(lines and " know the bits are a double and would print them as a huge integer. Uses the\n") + set ok to append_list(lines and " same %.15g format as `display` so a float reads identically both ways. */\n") + set ok to append_list(lines and "long long ep_float_to_string(long long bits) {\n") + set ok to append_list(lines and " double d;\n") + set ok to append_list(lines and " memcpy(&d, &bits, sizeof(double));\n") + set ok to append_list(lines and " char* buf = (char*)malloc(40);\n") + set ok to append_list(lines and " snprintf(buf, 40, \"%.15g\", d);\n") + set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n") + set ok to append_list(lines and " return (long long)buf;\n") + set ok to append_list(lines and "}\n") + set ok to append_list(lines and "\n") + set ok to append_list(lines and "long long ep_random_int(long long min, long long max) {\n") set ok to append_list(lines and " if (max <= min) return min;\n") set ok to append_list(lines and " /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n") set ok to append_list(lines and " unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n") @@ -4760,6 +4774,10 @@ define ep_rt_core_30: set ok to append_list(lines and " size_t len = strlen((const char*)data);\n") set ok to append_list(lines and "\n") set ok to append_list(lines and " unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;\n") + return join_strings(lines) + +define ep_rt_core_31: + set lines to create_list() set ok to append_list(lines and " size_t new_len = len + 1;\n") set ok to append_list(lines and " while (new_len % 64 != 56) new_len++;\n") set ok to append_list(lines and " unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);\n") @@ -4774,10 +4792,6 @@ define ep_rt_core_30: set ok to append_list(lines and " w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n") set ok to append_list(lines and " ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n") set ok to append_list(lines and " }\n") - return join_strings(lines) - -define ep_rt_core_31: - set lines to create_list() set ok to append_list(lines and " for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n") set ok to append_list(lines and " unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n") set ok to append_list(lines and " for (int i = 0; i < 80; i++) {\n") diff --git a/examples/bakery.ep b/examples/bakery.ep new file mode 100644 index 0000000..5887e72 --- /dev/null +++ b/examples/bakery.ep @@ -0,0 +1,300 @@ +# ═══════════════════════════════════════════════════════════════════ +# The Plainville Bakery — the Ernos flagship demo +# +# One ordinary morning at a bakery, written in plain English and +# exercising the whole language: +# +# structs + methods traits + implement enums + check +# try / Result errors closures + HOF custom iterators +# async ovens (event loop) spawned workers/channels floats + ints +# f-strings lists + maps file I/O +# sha256 receipts recursion break / continue +# English aliases: equals, is not equal to, and also, or else, not +# +# Compile and run (either compiler — the output is identical): +# ernos examples/bakery.ep && ./examples/bakery +# ./epc examples/bakery.ep && ./bakery +# ═══════════════════════════════════════════════════════════════════ + +import "collections" + +# ───────────────────────── The goods ───────────────────────── + +define structure Pastry: + field name as Str + field price_cents as Int + field stock as Int + +define trait Describable: + define describe returning Str + +implement Describable for Pastry: + define describe returning Str: + return f"{self.name} ({self.price_cents} cents, {self.stock} left)" + +# A sale either succeeds with a total, or tells you plainly why not. +define choice Outcome: + variant Sold with total as Int + variant Refused with reason as Str + +define sell on Pastry with qty as Int returning Outcome: + if qty < 1: + return Refused with "you have to buy at least one" + if qty > self.stock: + return Refused with "not enough in the case" + set self.stock to self.stock - qty + return Sold with self.price_cents * qty + +# ───────────────────────── Payments ───────────────────────── + +define choice Payment: + variant Cash + variant Card with last_digits as Int + +define payment_label with p as Payment returning Str: + check p: + if Cash: + return "cash" + if Card with digits: + return f"card ending {digits}" + return "unknown" + +# ─────────────────── Errors, the honest way ─────────────────── + +define choice Result: + variant Ok with value as Int + variant Error with message as Str + +define parse_quantity with wanted as Int returning Result: + if wanted < 0: + return Error with "quantity cannot be negative" + if wanted > 12: + return Error with "a dozen is the most one basket holds" + return Ok with wanted + +# `try` unwraps an Ok or passes the Error straight up to the caller. +define checked_quantity with wanted as Int returning Result: + set qty to try parse_quantity(wanted) + return Ok with qty + +# ────────────────── Loyalty points (recursion) ────────────────── +# Visit points grow like fibonacci — old customers are treasured. +# Declared-Int params make this run at C speed: no GC bookkeeping. + +define loyalty_points with visits as Int returning Int: + if visits < 2: + return visits + return loyalty_points(visits - 1) + loyalty_points(visits - 2) + +# ─────────────── Discounts are just closures ─────────────── + +define apply_discount with rule and cents as Int returning Int: + return rule(cents) + +# ─────────────── A queue that knows how to count ─────────────── + +define structure TicketRoll: + field current as Int + field last as Int + +implement Iterator for TicketRoll: + define next returning IterResult: + if self.current > self.last: + return Done + set val to self.current + set self.current to self.current + 1 + return Next with val + +# ─────────────────── The ovens run async ─────────────────── +# Each bake yields to the event loop while it waits, so all three +# trays share one thread and still come out in delay order. + +async define bake with item as Str and minutes as Int returning Str: + display f" oven: {item} goes in" + set dummy to await sleep_ms(minutes) + display f" oven: {item} comes out golden" + return item + +# ──────────────── The accountants work in parallel ──────────────── + +define add_up with a as Int and b as Int and ch: + send a + b to ch + return 0 + +# ──────────────── Plain-English insertion sort ──────────────── + +define sort_ascending with values: + set i to 1 + repeat while i < length_list(values): + set key to get_list(values and i) + set j to i - 1 + repeat while j >= 0: + if get_list(values and j) > key: + set ok to set_list(values and j + 1 and get_list(values and j)) + set j to j - 1 + else: + break + set ok to set_list(values and j + 1 and key) + set i to i + 1 + return 0 + +# ═══════════════════════ Opening time ═══════════════════════ + +define main: + display "═══ The Plainville Bakery ═══" + display "" + + # ---- the morning case (structs in a list) ---- + set croissant to create Pastry: + name is "Croissant" + price_cents is 350 + stock is 6 + set scone to create Pastry: + name is "Scone" + price_cents is 275 + stock is 4 + set cinnamon_roll to create Pastry: + name is "Cinnamon Roll" + price_cents is 425 + stock is 3 + + display "In the case this morning:" + display concat(" " and croissant.describe()) + display concat(" " and scone.describe()) + display concat(" " and cinnamon_roll.describe()) + display "" + + # ---- ticket numbers via the custom iterator ---- + display "Tickets torn off the roll:" + set roll to create TicketRoll: + current is 101 + last is 104 + for each ticket in roll: + display f" ticket {ticket}" + display "" + + # ---- orders: enums, check, try/Result, English aliases ---- + display "Orders:" + + set order_qty to checked_quantity(2) + check order_qty: + if Ok with qty: + set sale to croissant.sell(qty) + check sale: + if Sold with total: + display f" 2 croissants sold for {total} cents" + if Refused with reason: + display concat(" refused: " and reason) + if Error with message: + display concat(" bad order: " and message) + + set too_many to checked_quantity(15) + check too_many: + if Ok with qty: + display " this should not happen" + if Error with message: + display concat(" bad order: " and message) + + set greedy to scone.sell(9) + check greedy: + if Sold with total: + display " this should not happen either" + if Refused with reason: + display concat(" refused: " and reason) + + if not (croissant.stock equals 6): + display f" the case shows {croissant.stock} croissants now" + display "" + + # ---- payments ---- + set first_payment to Card with 4242 + set second_payment to Cash + display concat("First customer paid by " and payment_label(first_payment)) + display concat("Second customer paid by " and payment_label(second_payment)) + display "" + + # ---- discounts as closures (single-line and block form) ---- + set regulars_discount to given cents: return cents - cents / 10 + set staff_discount to given cents: + set half to cents / 2 + return half + set full_price to 700 + display f"Regulars pay {apply_discount(regulars_discount and full_price)} of {full_price} cents" + display f"Staff pay {apply_discount(staff_discount and full_price)} of {full_price} cents" + display "" + + # ---- loyalty points (recursion at C speed) ---- + set visits to 20 + display f"A customer on visit {visits} has {loyalty_points(visits)} loyalty points" + display "" + + # ---- floats: the day's temperatures ---- + set morning_temp to 219.5 + set afternoon_temp to 221.5 + set average_temp to (morning_temp + afternoon_temp) / 2.0 + display f"Average oven temperature: {average_temp}" + display "" + + # ---- async baking: three trays, one thread ---- + display "Afternoon bake (async event loop):" + set tray1 to bake("sourdough" and 30) + set tray2 to bake("baguettes" and 10) + set tray3 to bake("focaccia" and 20) + set done1 to await tray1 + set done2 to await tray2 + set done3 to await tray3 + display f" racks filled: {done1}, {done2}, {done3}" + display "" + + # ---- the accountants: spawned threads and channels ---- + set morning_ch to channel + set afternoon_ch to channel + spawn add_up(700 and 550 and morning_ch) + spawn add_up(425 and 350 and afternoon_ch) + set morning_total to receive from morning_ch + set afternoon_total to receive from afternoon_ch + set day_total to morning_total + afternoon_total + display f"Morning till: {morning_total} cents" + display f"Afternoon till: {afternoon_total} cents" + display "" + + # ---- sort the day's sale amounts, plain-English algorithm ---- + set sales to [550, 350, 700, 425] + set ok to sort_ascending(sales) + display "Today's sales, smallest to largest:" + set idx to 0 + repeat while idx < length_list(sales): + set amount to get_list(sales and idx) + if amount equals 425: + set idx to idx + 1 + continue + display f" {amount} cents" + set idx to idx + 1 + display " (the 425 stays between us — it was the owner's snack)" + display "" + + # ---- the ledger: a map of category totals ---- + set ledger to create_map() + set ok to map_insert(ledger and "bread" and morning_total) + set ok to map_insert(ledger and "pastry" and afternoon_total) + set bread_cents to map_get_val(ledger and "bread") + set pastry_cents to map_get_val(ledger and "pastry") + display f"Ledger holds {map_size(ledger)} categories" + display f" bread: {bread_cents} cents" + display f" pastry: {pastry_cents} cents" + display "" + + # ---- the daily report: written to disk, read back ---- + set report to f"Plainville Bakery daily report\ntotal: {day_total} cents\n" + set ok to file_write("bakery_report.txt" and report) + set read_back to file_read("bakery_report.txt") + display "Report on file:" + display read_back + + # ---- a tamper-evident receipt: sha256 ---- + set receipt_stamp to ep_sha256(report) + display concat("Receipt stamp: " and substring(receipt_stamp and 0 and 16)) + display "" + + display f"Day's total: {day_total} cents. The ovens rest." + return 0 diff --git a/runtime/ep_runtime.c b/runtime/ep_runtime.c index cd69b7e..ec96bd4 100644 --- a/runtime/ep_runtime.c +++ b/runtime/ep_runtime.c @@ -1776,6 +1776,7 @@ long long channel_try_recv(long long chan_ptr, long long out_ptr); long long channel_has_data(long long chan_ptr); long long channel_select(long long channels_list, long long timeout_ms); long long ep_auto_to_string(long long val); +long long ep_float_to_string(long long bits); typedef struct EpChannel_ { long long* data; @@ -4497,6 +4498,19 @@ long long ep_auto_to_string(long long val) { return (long long)buf; } +/* Format a Float (double bits carried in a long long) as a string. F-string + interpolation routes Float-typed expressions here: ep_auto_to_string cannot + know the bits are a double and would print them as a huge integer. Uses the + same %.15g format as `display` so a float reads identically both ways. */ +long long ep_float_to_string(long long bits) { + double d; + memcpy(&d, &bits, sizeof(double)); + char* buf = (char*)malloc(40); + snprintf(buf, 40, "%.15g", d); + ep_gc_register(buf, EP_OBJ_STRING); + return (long long)buf; +} + long long ep_random_int(long long min, long long max) { if (max <= min) return min; /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */ diff --git a/src/codegen.rs b/src/codegen.rs index 5698ea0..799f5b5 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -537,6 +537,7 @@ impl Codegen { self.func_return_types.insert("channel_has_data".to_string(), Type::Int); self.func_return_types.insert("channel_select".to_string(), Type::Int); self.func_return_types.insert("ep_auto_to_string".to_string(), Type::DynStr); + self.func_return_types.insert("ep_float_to_string".to_string(), Type::DynStr); self.func_return_types.insert("string_upper".to_string(), Type::DynStr); self.func_return_types.insert("string_lower".to_string(), Type::DynStr); self.func_return_types.insert("string_trim".to_string(), Type::DynStr); @@ -2072,6 +2073,16 @@ impl Codegen { Ok(format!("({} {} {})", left_str, op_str, right_str)) } ExprNode::Call(name, args) => { + // F-string interpolation of a Float: ep_auto_to_string would print + // the double's raw bits as an integer, so route to the float + // formatter (same %.15g as `display`). + if name == "ep_auto_to_string" && args.len() == 1 + && self.infer_type(&args[0], var_types) == Type::Float + { + let inner = self.gen_expr(&args[0], var_types)?; + return Ok(format!("ep_float_to_string({})", inner)); + } + let mut args_str = Vec::new(); for arg in args { args_str.push(self.gen_expr(arg, var_types)?); diff --git a/src/type_check.rs b/src/type_check.rs index e1196da..a3ab3b5 100644 --- a/src/type_check.rs +++ b/src/type_check.rs @@ -931,6 +931,7 @@ impl TypeChecker { self.func_types.insert("display".into(), (vec![MonoType::Int], MonoType::Unit)); self.func_types.insert("display_string".into(), (vec![MonoType::Str], MonoType::Unit)); self.func_types.insert("ep_auto_to_string".into(), (vec![MonoType::Int], MonoType::DynStr)); + self.func_types.insert("ep_float_to_string".into(), (vec![MonoType::Any], MonoType::DynStr)); // Memory management let v12 = self.fresh_var(); diff --git a/tests/differential/fstring_float.ep b/tests/differential/fstring_float.ep new file mode 100644 index 0000000..652d2af --- /dev/null +++ b/tests/differential/fstring_float.ep @@ -0,0 +1,7 @@ +define main: + set a to 219.5 + set b to 221.5 + set avg to (a + b) / 2.0 + display f"average: {avg}" + display avg + return 0