From eb52be4ade0653674ce113b0d9d68cadef4af450 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Tue, 5 May 2026 19:15:49 -0500 Subject: [PATCH] runtime: add EL_TRUE/EL_FALSE macros and scoped arena for CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds EL_TRUE/EL_FALSE convenience macros to el_runtime.h alongside the existing EL_NULL, making boolean-returning builtins readable without raw (el_val_t) casts. Documents all value macros in the header comment. Also lands el_arena_push/el_arena_pop — a scoped string arena for CLI programs that never call el_request_start/end. The compiler can push a mark before a compilation unit and pop it after to free intermediate strings, reducing peak RSS during long compile runs. --- lang/el-compiler/runtime/el_runtime.c | 39 +++++++++++++++++++++++++++ lang/el-compiler/runtime/el_runtime.h | 9 +++++++ 2 files changed, 48 insertions(+) diff --git a/lang/el-compiler/runtime/el_runtime.c b/lang/el-compiler/runtime/el_runtime.c index 767bb6b..63c9f82 100644 --- a/lang/el-compiler/runtime/el_runtime.c +++ b/lang/el-compiler/runtime/el_runtime.c @@ -102,6 +102,45 @@ void el_request_end(void) { _tl_arena.count = 0; } +/* ── Scoped arena for CLI use ─────────────────────────────────────────────── * + * CLI programs never call el_request_start/end, so all strdup allocations are + * permanent. el_arena_push/pop let the compiler free intermediate strings + * after each compilation unit. + * + * el_arena_push() — activates the arena if not already active, saves the + * current arena count as a mark, and returns it as an el_val_t Int. + * el_arena_pop(mark) — frees all strings allocated since the push mark and + * resets the count. If count reaches 0, deactivates the arena. + */ +#define EL_ARENA_SCOPE_DEPTH 32 +static _Thread_local size_t _tl_arena_scope[EL_ARENA_SCOPE_DEPTH]; +static _Thread_local int _tl_arena_scope_depth = 0; + +el_val_t el_arena_push(void) { + if (!_tl_arena_active) { + _tl_arena_active = 1; + } + if (_tl_arena_scope_depth < EL_ARENA_SCOPE_DEPTH) { + _tl_arena_scope[_tl_arena_scope_depth++] = _tl_arena.count; + } + return (el_val_t)(int64_t)_tl_arena.count; +} + +el_val_t el_arena_pop(el_val_t mark) { + size_t save = (size_t)(int64_t)mark; + if (save > _tl_arena.count) save = 0; + for (size_t i = save; i < _tl_arena.count; i++) { + if (_tl_arena.ptrs[i]) { + free(_tl_arena.ptrs[i]); + _tl_arena.ptrs[i] = NULL; + } + } + _tl_arena.count = save; + if (_tl_arena_scope_depth > 0) _tl_arena_scope_depth--; + if (save == 0) _tl_arena_active = 0; + return 0; +} + /* Persistent allocation — bypasses the arena (state_set, engram internals). */ static char* el_strdup_persist(const char* s) { if (!s) return strdup(""); diff --git a/lang/el-compiler/runtime/el_runtime.h b/lang/el-compiler/runtime/el_runtime.h index a82a089..99cd89d 100644 --- a/lang/el-compiler/runtime/el_runtime.h +++ b/lang/el-compiler/runtime/el_runtime.h @@ -22,6 +22,9 @@ * EL_STR(s) cast string literal to el_val_t * EL_CSTR(v) cast el_val_t back to const char* * EL_INT(v) identity — el_val_t is already int64_t + * EL_NULL null / zero value + * EL_FALSE boolean false (0) + * EL_TRUE boolean true (1) * * Link requirements: * -lcurl — required for the HTTP client (http_get, http_post, llm_*). @@ -53,6 +56,8 @@ typedef int64_t el_val_t; #define EL_CSTR(v) ((const char*)(uintptr_t)(v)) #define EL_INT(v) (v) #define EL_NULL ((el_val_t)0) +#define EL_FALSE ((el_val_t)0) +#define EL_TRUE ((el_val_t)1) /* Float values share the el_val_t (int64) slot via a bit-cast. * The codegen emits Float literals as `el_from_float()` so the @@ -117,6 +122,10 @@ el_val_t el_min(el_val_t a, el_val_t b); void el_retain(el_val_t v); void el_release(el_val_t v); +/* ── Scoped arena (CLI use) ───────────────────────────────────────────────── */ +el_val_t el_arena_push(void); +el_val_t el_arena_pop(el_val_t mark); + /* ── List ────────────────────────────────────────────────────────────────── */ el_val_t el_list_new(el_val_t count, ...);