runtime: add EL_TRUE/EL_FALSE macros and scoped arena for CLI

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.
This commit is contained in:
Will Anderson
2026-05-05 19:15:49 -05:00
parent 7f295bffe9
commit eb52be4ade
2 changed files with 48 additions and 0 deletions
+39
View File
@@ -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("");
+9
View File
@@ -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(<dbl>)` 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, ...);