/* * el_seed.c — El language seed runtime: minimal C OS boundary * * This file exposes all OS-boundary primitives that El programs need, under * the __ prefix convention. It is self-contained: all allocators, arena * management, and el_request_start / el_request_end are defined here. * * Threading: __thread_create / __thread_join use dlsym(RTLD_DEFAULT) to look * up El function symbols at runtime. This is the foundation of El's parallelism. * * Link: cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \ * -o .c el_seed.c */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "el_seed.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ── Private allocator ───────────────────────────────────────────────────── */ /* * el_seed.c carries its own arena for per-request allocation tracking. * The arena is reset at el_request_start / el_request_end, which are defined * below and delegate to seed_request_start / seed_request_end. */ #define SEED_ARENA_INITIAL 512 typedef struct { char** ptrs; size_t count; size_t cap; } SeedArena; static _Thread_local SeedArena _seed_arena = {NULL, 0, 0}; static _Thread_local int _seed_arena_on = 0; static void seed_arena_track(char* p) { if (!_seed_arena_on || !p) return; if (_seed_arena.count >= _seed_arena.cap) { size_t nc = _seed_arena.cap == 0 ? SEED_ARENA_INITIAL : _seed_arena.cap * 2; char** g = realloc(_seed_arena.ptrs, nc * sizeof(char*)); if (!g) return; _seed_arena.ptrs = g; _seed_arena.cap = nc; } _seed_arena.ptrs[_seed_arena.count++] = p; } static void seed_request_start(void) { _seed_arena.count = 0; _seed_arena_on = 1; } static void seed_request_end(void) { _seed_arena_on = 0; for (size_t i = 0; i < _seed_arena.count; i++) free(_seed_arena.ptrs[i]); _seed_arena.count = 0; } /* el_request_start / el_request_end — formerly defined in el_runtime.c. * Now self-contained in el_seed.c, delegating to the seed arena. */ void el_request_start(void) { seed_request_start(); } void el_request_end(void) { seed_request_end(); } /* Persistent alloc — bypasses arena (state, engram internals). */ static char* seed_strdup_persist(const char* s) { if (!s) return strdup(""); return strdup(s); } static char* seed_strdup(const char* s) { char* p = strdup(s ? s : ""); seed_arena_track(p); return p; } static char* seed_strbuf(size_t n) { char* p = malloc(n + 1); if (!p) { fputs("el_seed: out of memory\n", stderr); exit(1); } p[0] = '\0'; seed_arena_track(p); return p; } static el_val_t seed_wrap_str(char* s) { return EL_STR(s); } /* ── String primitives ───────────────────────────────────────────────────── */ el_val_t __str_len(el_val_t s) { const char* p = EL_CSTR(s); if (!p) return 0; return (el_val_t)strlen(p); } el_val_t __str_char_at(el_val_t s, el_val_t i) { const char* p = EL_CSTR(s); if (!p) return 0; int64_t len = (int64_t)strlen(p); int64_t idx = (int64_t)i; if (idx < 0 || idx >= len) return 0; return (el_val_t)(unsigned char)p[idx]; } el_val_t __str_alloc(el_val_t n) { int64_t sz = (int64_t)n; if (sz < 0) sz = 0; char* buf = seed_strbuf((size_t)sz); memset(buf, 0, (size_t)sz + 1); return seed_wrap_str(buf); } el_val_t __str_set_char(el_val_t s, el_val_t i, el_val_t c) { char* p = (char*)(uintptr_t)s; if (!p) return s; int64_t len = (int64_t)strlen(p); int64_t idx = (int64_t)i; if (idx < 0 || idx >= len) return s; p[idx] = (char)(unsigned char)(int64_t)c; return s; } el_val_t __str_cmp(el_val_t a, el_val_t b) { const char* sa = EL_CSTR(a); const char* sb = EL_CSTR(b); if (!sa) sa = ""; if (!sb) sb = ""; return (el_val_t)strcmp(sa, sb); } el_val_t __str_ncmp(el_val_t a, el_val_t b, el_val_t n) { const char* sa = EL_CSTR(a); const char* sb = EL_CSTR(b); if (!sa) sa = ""; if (!sb) sb = ""; return (el_val_t)strncmp(sa, sb, (size_t)(int64_t)n); } el_val_t __str_concat_raw(el_val_t a, el_val_t b) { const char* sa = EL_CSTR(a); const char* sb = EL_CSTR(b); if (!sa) sa = ""; if (!sb) sb = ""; size_t la = strlen(sa), lb = strlen(sb); char* out = seed_strbuf(la + lb); memcpy(out, sa, la); memcpy(out + la, sb, lb); out[la + lb] = '\0'; return seed_wrap_str(out); } el_val_t __str_slice_raw(el_val_t s, el_val_t start, el_val_t end) { const char* p = EL_CSTR(s); if (!p) return seed_wrap_str(seed_strdup("")); int64_t len = (int64_t)strlen(p); int64_t st = (int64_t)start; int64_t en = (int64_t)end; if (st < 0) st = 0; if (en > len) en = len; if (st >= en) return seed_wrap_str(seed_strdup("")); int64_t sz = en - st; char* out = seed_strbuf((size_t)sz); memcpy(out, p + st, (size_t)sz); out[sz] = '\0'; return seed_wrap_str(out); } el_val_t __int_to_str(el_val_t n) { char buf[32]; snprintf(buf, sizeof(buf), "%lld", (long long)(int64_t)n); return seed_wrap_str(seed_strdup(buf)); } el_val_t __str_to_int(el_val_t s) { const char* p = EL_CSTR(s); if (!p) return 0; return (el_val_t)atoll(p); } el_val_t __float_to_str(el_val_t f) { char buf[64]; snprintf(buf, sizeof(buf), "%g", el_to_float(f)); return seed_wrap_str(seed_strdup(buf)); } el_val_t __str_to_float(el_val_t s) { const char* p = EL_CSTR(s); if (!p) return el_from_float(0.0); return el_from_float(strtod(p, NULL)); } /* ── I/O ─────────────────────────────────────────────────────────────────── */ void __println(el_val_t s) { const char* p = EL_CSTR(s); puts(p ? p : ""); } void __print(el_val_t s) { const char* p = EL_CSTR(s); if (p) fputs(p, stdout); } el_val_t __readline(void) { char buf[4096]; if (!fgets(buf, sizeof(buf), stdin)) return seed_wrap_str(seed_strdup("")); size_t len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; return seed_wrap_str(seed_strdup(buf)); } /* ── Filesystem ──────────────────────────────────────────────────────────── */ el_val_t __fs_read(el_val_t path) { const char* p = EL_CSTR(path); if (!p) return seed_wrap_str(seed_strdup("")); FILE* f = fopen(p, "rb"); if (!f) return seed_wrap_str(seed_strdup("")); fseek(f, 0, SEEK_END); long sz = ftell(f); rewind(f); if (sz < 0) { fclose(f); return seed_wrap_str(seed_strdup("")); } char* buf = seed_strbuf((size_t)sz); size_t got = fread(buf, 1, (size_t)sz, f); buf[got] = '\0'; fclose(f); return seed_wrap_str(buf); } el_val_t __fs_write(el_val_t path, el_val_t content) { const char* p = EL_CSTR(path); const char* c = EL_CSTR(content); if (!p || !c) return 0; FILE* f = fopen(p, "wb"); if (!f) return 0; size_t n = strlen(c); size_t w = fwrite(c, 1, n, f); fclose(f); return w == n ? 1 : 0; } el_val_t __fs_exists(el_val_t path) { const char* p = EL_CSTR(path); if (!p || !*p) return 0; struct stat st; return (el_val_t)(stat(p, &st) == 0 ? 1 : 0); } el_val_t __fs_list_raw(el_val_t path) { const char* p = EL_CSTR(path); if (!p) return seed_wrap_str(seed_strdup("")); DIR* d = opendir(p); if (!d) return seed_wrap_str(seed_strdup("")); /* Build newline-separated list of filenames. */ size_t cap = 4096, len = 0; char* buf = malloc(cap); if (!buf) { closedir(d); return seed_wrap_str(seed_strdup("")); } buf[0] = '\0'; struct dirent* e; while ((e = readdir(d)) != NULL) { if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; size_t nlen = strlen(e->d_name); while (len + nlen + 2 >= cap) { cap *= 2; char* g = realloc(buf, cap); if (!g) { free(buf); closedir(d); return seed_wrap_str(seed_strdup("")); } buf = g; } if (len > 0) buf[len++] = '\n'; memcpy(buf + len, e->d_name, nlen); len += nlen; buf[len] = '\0'; } closedir(d); seed_arena_track(buf); return seed_wrap_str(buf); } el_val_t __fs_mkdir(el_val_t path) { const char* p = EL_CSTR(path); if (!p || !*p) return 0; size_t n = strlen(p); char* buf = malloc(n + 1); if (!buf) return 0; memcpy(buf, p, n + 1); for (size_t i = 1; i <= n; i++) { if (buf[i] == '/' || buf[i] == '\0') { char saved = buf[i]; buf[i] = '\0'; if (buf[0] != '\0') { if (mkdir(buf, 0755) != 0 && errno != EEXIST) { struct stat st; if (stat(buf, &st) != 0 || !S_ISDIR(st.st_mode)) { free(buf); return 0; } } } buf[i] = saved; } } free(buf); return 1; } el_val_t __fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t n) { const char* p = EL_CSTR(path); const char* b = EL_CSTR(bytes); int64_t sz = (int64_t)n; if (!p || !b || sz < 0) return 0; FILE* f = fopen(p, "wb"); if (!f) return 0; size_t written = (sz > 0) ? fwrite(b, 1, (size_t)sz, f) : 0; int ok1 = (fflush(f) == 0); int ok2 = (fclose(f) == 0); if (!ok1 || !ok2 || written != (size_t)sz) { remove(p); return 0; } return 1; } /* ── HTTP client ─────────────────────────────────────────────────────────── */ typedef struct { char* data; size_t len; size_t cap; } SeedHttpBuf; static void seed_httpbuf_init(SeedHttpBuf* b) { b->cap = 1024; b->len = 0; b->data = malloc(b->cap); if (!b->data) { fputs("el_seed: out of memory\n", stderr); exit(1); } b->data[0] = '\0'; } static void seed_httpbuf_append(SeedHttpBuf* b, const void* src, size_t n) { while (b->len + n + 1 > b->cap) b->cap *= 2; b->data = realloc(b->data, b->cap); if (!b->data) { fputs("el_seed: out of memory\n", stderr); exit(1); } memcpy(b->data + b->len, src, n); b->len += n; b->data[b->len] = '\0'; } static size_t seed_http_write_cb(char* ptr, size_t size, size_t nmemb, void* ud) { size_t n = size * nmemb; seed_httpbuf_append((SeedHttpBuf*)ud, ptr, n); return n; } /* Build a curl_slist from a JSON object string of header name:value pairs. */ static struct curl_slist* seed_headers_from_json(const char* hj) { struct curl_slist* h = NULL; if (!hj || !*hj) return NULL; /* Walk key:value pairs at depth 1. Simple parser — same logic as json_find_key * in el_runtime.c but adapted for building curl headers. */ const char* p = hj; while (*p && *p != '{') p++; if (*p == '{') p++; while (*p) { while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',') p++; if (*p == '}' || *p == '\0') break; if (*p != '"') break; /* Parse key */ p++; const char* ks = p; while (*p && *p != '"') { if (*p == '\\') p++; p++; } size_t klen = (size_t)(p - ks); if (*p == '"') p++; /* Skip : */ while (*p == ' ' || *p == ':') p++; /* Parse value */ if (*p != '"') break; p++; const char* vs = p; while (*p && *p != '"') { if (*p == '\\') p++; p++; } size_t vlen = (size_t)(p - vs); if (*p == '"') p++; /* Build "Key: Value" header line */ size_t line_len = klen + 2 + vlen + 1; char* line = malloc(line_len); if (line) { memcpy(line, ks, klen); memcpy(line + klen, ": ", 2); memcpy(line + klen + 2, vs, vlen); line[klen + 2 + vlen] = '\0'; h = curl_slist_append(h, line); free(line); } } return h; } static el_val_t seed_http_error_json(const char* msg) { if (!msg) msg = "unknown error"; size_t n = strlen(msg) * 6 + 20; char* buf = seed_strbuf(n); /* Simple escape: replace " with \" */ char* d = buf; *d++ = '{'; *d++ = '"'; *d++ = 'e'; *d++ = 'r'; *d++ = 'r'; *d++ = 'o'; *d++ = 'r'; *d++ = '"'; *d++ = ':'; *d++ = '"'; for (const char* s = msg; *s; s++) { if (*s == '"' || *s == '\\') *d++ = '\\'; *d++ = *s; } *d++ = '"'; *d++ = '}'; *d = '\0'; return seed_wrap_str(buf); } el_val_t __http_do(el_val_t method, el_val_t url, el_val_t body, el_val_t headers_json, el_val_t timeout_ms) { const char* m = EL_CSTR(method); const char* u = EL_CSTR(url); const char* b = EL_CSTR(body); const char* hj = EL_CSTR(headers_json); int64_t tms = (int64_t)timeout_ms; if (tms <= 0) tms = 60000; if (!u || !*u) return seed_http_error_json("empty url"); CURL* c = curl_easy_init(); if (!c) return seed_http_error_json("curl_easy_init failed"); SeedHttpBuf rb; seed_httpbuf_init(&rb); char errbuf[CURL_ERROR_SIZE]; errbuf[0] = '\0'; curl_easy_setopt(c, CURLOPT_URL, u); curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, seed_http_write_cb); curl_easy_setopt(c, CURLOPT_WRITEDATA, &rb); curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, (long)tms); curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(c, CURLOPT_ERRORBUFFER, errbuf); curl_easy_setopt(c, CURLOPT_USERAGENT, "el-seed/1.0"); struct curl_slist* hdrs = seed_headers_from_json(hj); if (hdrs) curl_easy_setopt(c, CURLOPT_HTTPHEADER, hdrs); if (m && strcmp(m, "POST") == 0) { curl_easy_setopt(c, CURLOPT_POST, 1L); curl_easy_setopt(c, CURLOPT_POSTFIELDS, b ? b : ""); curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)(b ? strlen(b) : 0)); } else if (m && strcmp(m, "PUT") == 0) { curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, "PUT"); if (b) { curl_easy_setopt(c, CURLOPT_POSTFIELDS, b); curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)strlen(b)); } } else if (m && strcmp(m, "DELETE") == 0) { curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, "DELETE"); } else if (m && strcmp(m, "PATCH") == 0) { curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, "PATCH"); if (b) { curl_easy_setopt(c, CURLOPT_POSTFIELDS, b); curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)strlen(b)); } } /* GET is the default */ CURLcode rc = curl_easy_perform(c); if (hdrs) curl_slist_free_all(hdrs); curl_easy_cleanup(c); if (rc != CURLE_OK) { free(rb.data); const char* em = errbuf[0] ? errbuf : curl_easy_strerror(rc); return seed_http_error_json(em); } seed_arena_track(rb.data); return seed_wrap_str(rb.data); } static size_t seed_http_file_write_cb(char* ptr, size_t size, size_t nmemb, void* ud) { return fwrite(ptr, size, nmemb, (FILE*)ud); } el_val_t __http_do_to_file(el_val_t method, el_val_t url, el_val_t body, el_val_t headers_json, el_val_t out_path) { const char* m = EL_CSTR(method); const char* u = EL_CSTR(url); const char* b = EL_CSTR(body); const char* hj = EL_CSTR(headers_json); const char* op = EL_CSTR(out_path); if (!u || !*u || !op || !*op) return 0; FILE* f = fopen(op, "wb"); if (!f) return 0; CURL* c = curl_easy_init(); if (!c) { fclose(f); remove(op); return 0; } char errbuf[CURL_ERROR_SIZE]; errbuf[0] = '\0'; curl_easy_setopt(c, CURLOPT_URL, u); curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, seed_http_file_write_cb); curl_easy_setopt(c, CURLOPT_WRITEDATA, f); curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, 60000L); curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(c, CURLOPT_ERRORBUFFER, errbuf); curl_easy_setopt(c, CURLOPT_USERAGENT, "el-seed/1.0"); struct curl_slist* hdrs = seed_headers_from_json(hj); if (hdrs) curl_easy_setopt(c, CURLOPT_HTTPHEADER, hdrs); if (m && strcmp(m, "POST") == 0) { curl_easy_setopt(c, CURLOPT_POST, 1L); curl_easy_setopt(c, CURLOPT_POSTFIELDS, b ? b : ""); curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)(b ? strlen(b) : 0)); } CURLcode rc = curl_easy_perform(c); if (hdrs) curl_slist_free_all(hdrs); curl_easy_cleanup(c); int ok1 = (fflush(f) == 0); int ok2 = (fclose(f) == 0); if (rc != CURLE_OK || !ok1 || !ok2) { remove(op); return 0; } return 1; } /* ── HTTP server ─────────────────────────────────────────────────────────── */ /* Delegate to el_runtime.c's http_serve / http_serve_v2 via the existing * http_set_handler mechanism. */ /* Forward declarations for el_runtime.c functions called from el_seed.c. * A full #include "el_runtime.h" causes redefinition conflicts (el_seed.h * already defines el_to_float, el_from_float, and several __ wrappers). * Declare only the symbols actually used here. */ /* HTTP server */ el_val_t http_serve(el_val_t port, el_val_t handler); el_val_t http_serve_v2(el_val_t port, el_val_t handler); el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body); /* JSON */ el_val_t json_get(el_val_t json, el_val_t key); el_val_t json_get_raw(el_val_t json_str, el_val_t key); el_val_t json_parse(el_val_t s); el_val_t json_stringify(el_val_t v); el_val_t json_array_len(el_val_t json_str); el_val_t json_array_get(el_val_t json_str, el_val_t index); el_val_t json_array_get_string(el_val_t json_str, el_val_t index); el_val_t json_get_string(el_val_t json_str, el_val_t key); el_val_t json_get_int(el_val_t json_str, el_val_t key); el_val_t json_get_float(el_val_t json_str, el_val_t key); el_val_t json_get_bool(el_val_t json_str, el_val_t key); el_val_t json_set(el_val_t json_str, el_val_t key, el_val_t value); /* URL / HTML */ el_val_t url_encode(el_val_t s); el_val_t url_decode(el_val_t s); el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json); /* State K/V */ el_val_t state_set(el_val_t key, el_val_t value); el_val_t state_get(el_val_t key); el_val_t state_del(el_val_t key); el_val_t state_keys(void); el_val_t state_get_or(el_val_t key, el_val_t default_val); /* Engram graph */ el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience); el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t importance, el_val_t confidence, el_val_t tier, el_val_t tags_json); el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t importance, el_val_t confidence, el_val_t tier, el_val_t tags_json, el_val_t layer_id); el_val_t engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible, el_val_t transparent, el_val_t injectable); el_val_t engram_remove_layer(el_val_t layer_id); el_val_t engram_list_layers(void); el_val_t engram_get_node(el_val_t id); void engram_strengthen(el_val_t node_id); void engram_forget(el_val_t node_id); el_val_t engram_node_count(void); el_val_t engram_search(el_val_t query, el_val_t limit); el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset); void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation); el_val_t engram_edge_between(el_val_t from_id, el_val_t to_id); el_val_t engram_neighbors(el_val_t node_id); el_val_t engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction); el_val_t engram_edge_count(void); el_val_t engram_activate(el_val_t query, el_val_t depth); el_val_t engram_save(el_val_t path); el_val_t engram_load(el_val_t path); el_val_t engram_get_node_json(el_val_t id); el_val_t engram_search_json(el_val_t query, el_val_t limit); el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset); el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset); el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction); el_val_t engram_activate_json(el_val_t query, el_val_t depth); el_val_t engram_stats_json(void); el_val_t engram_list_layers_json(void); el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth); void __http_serve(el_val_t port, el_val_t handler_name) { http_serve(port, handler_name); } void __http_serve_v2(el_val_t port, el_val_t handler_name) { http_serve_v2(port, handler_name); } el_val_t __http_response(el_val_t status, el_val_t headers_json, el_val_t body) { return http_response(status, headers_json, body); } /* ── HTTP SSE — Server-Sent Events streaming ─────────────────────────────── */ /* * Thread-local file descriptor stashed by http_worker_v2 before calling the * El handler. El SSE builtins read this to get the raw socket fd. * * Lifecycle: * http_worker_v2 sets _tl_http_conn_fd = fd (via el_seed_set_http_conn_fd) * El handler calls __http_conn_fd() → receives that fd * El handler calls __http_sse_open(fd) → sends SSE headers, keeps fd open * El handler calls __http_sse_send(fd, data) → writes "data: ...\n\n" * El handler calls __http_sse_close(fd) → closes the fd * El handler returns "__sse__" sentinel → http_worker_v2 does NOT close fd * * The -1 value means no current connection (guard against misuse outside * a handler context). */ static __thread int _tl_http_conn_fd = -1; /* Called by el_runtime.c's http_worker_v2 — not part of the El ABI. */ void el_seed_set_http_conn_fd(int fd) { _tl_http_conn_fd = fd; } /* __http_conn_fd() — returns the raw fd for the current HTTP connection. * Valid only inside an http_serve_v2 handler before it returns. */ el_val_t __http_conn_fd(void) { return EL_INT(_tl_http_conn_fd); } /* __http_sse_open(fd) — sends SSE response headers on fd, keeping it open. * Returns 1 on success, 0 on write failure. */ el_val_t __http_sse_open(el_val_t conn_id) { int fd = (int)(int64_t)conn_id; if (fd < 0) return 0; static const char sse_headers[] = "HTTP/1.1 200 OK\r\n" "Content-Type: text/event-stream\r\n" "Cache-Control: no-cache\r\n" "Connection: keep-alive\r\n" "Access-Control-Allow-Origin: *\r\n" "\r\n"; size_t n = sizeof(sse_headers) - 1; /* exclude NUL */ size_t sent = 0; while (sent < n) { ssize_t w = write(fd, sse_headers + sent, n - sent); if (w <= 0) return 0; sent += (size_t)w; } return 1; } /* __http_sse_send(fd, data) — writes one SSE event frame: "data: \n\n". * data must not contain newlines. Returns 1 on success, 0 on client disconnect. */ el_val_t __http_sse_send(el_val_t conn_id, el_val_t data) { int fd = (int)(int64_t)conn_id; if (fd < 0) return 0; const char* s = EL_CSTR(data); if (!s) s = ""; /* Build "data: \n\n" in a single buffer for one write call. */ size_t prefix_len = 6; /* "data: " */ size_t slen = strlen(s); size_t total = prefix_len + slen + 2; /* + "\n\n" */ char* buf = malloc(total + 1); if (!buf) return 0; memcpy(buf, "data: ", 6); memcpy(buf + 6, s, slen); buf[6 + slen] = '\n'; buf[6 + slen + 1] = '\n'; buf[total] = '\0'; size_t sent = 0; int ok = 1; while (sent < total) { ssize_t w = write(fd, buf + sent, total - sent); if (w <= 0) { ok = 0; break; } sent += (size_t)w; } free(buf); return ok ? 1 : 0; } /* __http_sse_close(fd) — closes the SSE connection fd. */ el_val_t __http_sse_close(el_val_t conn_id) { int fd = (int)(int64_t)conn_id; if (fd < 0) return 0; close(fd); return 1; } /* ── Threading ───────────────────────────────────────────────────────────── */ /* * Design: * Static ElThread table (max EL_SEED_MAX_THREADS entries). * __thread_create: pick a free slot, store fn_name + arg, launch pthread. * Worker: dlsym(RTLD_DEFAULT, fn_name) → call as el_val_t fn(el_val_t). * Store result string in slot.result. * __thread_join: pthread_join, return stored result string. * * Thread handle is the slot index (0..EL_SEED_MAX_THREADS-1). * Returns -1 on failure (no slots, dlsym failure, pthread_create failure). * * Each slot is guarded by its own mutex so join/create on different handles * never contend. */ #define EL_SEED_MAX_THREADS 64 typedef el_val_t (*ElFn1)(el_val_t); typedef struct { int in_use; char* fn_name; char* arg; char* result; pthread_t tid; int done; /* set to 1 by worker before exit */ } ElThread; static ElThread _el_threads[EL_SEED_MAX_THREADS]; static pthread_mutex_t _el_thread_mu = PTHREAD_MUTEX_INITIALIZER; typedef struct { int slot; } ElThreadArg; static void* el_thread_worker(void* raw) { ElThreadArg* ta = (ElThreadArg*)raw; int slot = ta->slot; free(ta); ElThread* t = &_el_threads[slot]; /* Resolve the El function symbol in the running binary. */ void* sym = dlsym(RTLD_DEFAULT, t->fn_name); if (!sym) { pthread_mutex_lock(&_el_thread_mu); t->result = seed_strdup_persist(""); t->done = 1; pthread_mutex_unlock(&_el_thread_mu); return NULL; } ElFn1 fn = (ElFn1)sym; /* Call the El function with the string argument. */ el_val_t arg_val = EL_STR(t->arg); el_val_t ret = fn(arg_val); /* Persist the result string. */ const char* rs = EL_CSTR(ret); char* stored = seed_strdup_persist(rs ? rs : ""); pthread_mutex_lock(&_el_thread_mu); t->result = stored; t->done = 1; pthread_mutex_unlock(&_el_thread_mu); return NULL; } el_val_t __thread_create(el_val_t fn_name, el_val_t arg) { const char* fname = EL_CSTR(fn_name); const char* astr = EL_CSTR(arg); if (!fname || !*fname) return (el_val_t)(int64_t)-1; if (!astr) astr = ""; pthread_mutex_lock(&_el_thread_mu); int slot = -1; for (int i = 0; i < EL_SEED_MAX_THREADS; i++) { if (!_el_threads[i].in_use) { slot = i; break; } } if (slot < 0) { pthread_mutex_unlock(&_el_thread_mu); return (el_val_t)(int64_t)-1; } ElThread* t = &_el_threads[slot]; t->in_use = 1; t->done = 0; t->fn_name = seed_strdup_persist(fname); t->arg = seed_strdup_persist(astr); t->result = NULL; pthread_mutex_unlock(&_el_thread_mu); ElThreadArg* ta = malloc(sizeof(ElThreadArg)); if (!ta) { pthread_mutex_lock(&_el_thread_mu); free(t->fn_name); free(t->arg); t->in_use = 0; pthread_mutex_unlock(&_el_thread_mu); return (el_val_t)(int64_t)-1; } ta->slot = slot; if (pthread_create(&t->tid, NULL, el_thread_worker, ta) != 0) { free(ta); pthread_mutex_lock(&_el_thread_mu); free(t->fn_name); free(t->arg); t->in_use = 0; pthread_mutex_unlock(&_el_thread_mu); return (el_val_t)(int64_t)-1; } return (el_val_t)(int64_t)slot; } el_val_t __thread_join(el_val_t tid) { int64_t slot = (int64_t)tid; if (slot < 0 || slot >= EL_SEED_MAX_THREADS) return seed_wrap_str(seed_strdup("")); ElThread* t = &_el_threads[slot]; pthread_mutex_lock(&_el_thread_mu); if (!t->in_use) { pthread_mutex_unlock(&_el_thread_mu); return seed_wrap_str(seed_strdup("")); } pthread_mutex_unlock(&_el_thread_mu); /* Wait for thread to finish. */ pthread_join(t->tid, NULL); pthread_mutex_lock(&_el_thread_mu); char* res = t->result ? t->result : ""; char* copy = seed_strdup(res); /* arena-tracked for caller lifetime */ free(t->fn_name); free(t->arg); if (t->result) { free(t->result); t->result = NULL; } t->fn_name = NULL; t->arg = NULL; t->in_use = 0; t->done = 0; pthread_mutex_unlock(&_el_thread_mu); return seed_wrap_str(copy); } /* ── Mutex pool ──────────────────────────────────────────────────────────── */ #define EL_SEED_MAX_MUTEXES 256 static pthread_mutex_t _el_mutexes[EL_SEED_MAX_MUTEXES]; static int _el_mutex_init[EL_SEED_MAX_MUTEXES]; static int _el_mutex_used[EL_SEED_MAX_MUTEXES]; static pthread_mutex_t _el_mutex_pool_mu = PTHREAD_MUTEX_INITIALIZER; el_val_t __mutex_new(void) { pthread_mutex_lock(&_el_mutex_pool_mu); int slot = -1; for (int i = 0; i < EL_SEED_MAX_MUTEXES; i++) { if (!_el_mutex_used[i]) { slot = i; break; } } if (slot < 0) { pthread_mutex_unlock(&_el_mutex_pool_mu); return (el_val_t)(int64_t)-1; } _el_mutex_used[slot] = 1; if (!_el_mutex_init[slot]) { pthread_mutex_init(&_el_mutexes[slot], NULL); _el_mutex_init[slot] = 1; } pthread_mutex_unlock(&_el_mutex_pool_mu); return (el_val_t)(int64_t)slot; } void __mutex_lock(el_val_t m) { int64_t slot = (int64_t)m; if (slot < 0 || slot >= EL_SEED_MAX_MUTEXES) return; if (!_el_mutex_init[slot]) return; pthread_mutex_lock(&_el_mutexes[slot]); } void __mutex_unlock(el_val_t m) { int64_t slot = (int64_t)m; if (slot < 0 || slot >= EL_SEED_MAX_MUTEXES) return; if (!_el_mutex_init[slot]) return; pthread_mutex_unlock(&_el_mutexes[slot]); } /* ── Subprocess ──────────────────────────────────────────────────────────── */ el_val_t __exec(el_val_t cmd) { const char* c = EL_CSTR(cmd); if (!c) return seed_wrap_str(seed_strdup("")); FILE* f = popen(c, "r"); if (!f) return seed_wrap_str(seed_strdup("")); size_t cap = 4096, len = 0; char* buf = malloc(cap); if (!buf) { pclose(f); return seed_wrap_str(seed_strdup("")); } char tmp[4096]; while (fgets(tmp, sizeof(tmp), f)) { size_t n = strlen(tmp); while (len + n + 1 > cap) { cap *= 2; char* g = realloc(buf, cap); if (!g) { free(buf); pclose(f); return seed_wrap_str(seed_strdup("")); } buf = g; } memcpy(buf + len, tmp, n); len += n; buf[len] = '\0'; } pclose(f); seed_arena_track(buf); return seed_wrap_str(buf); } void __exec_bg(el_val_t cmd) { const char* c = EL_CSTR(cmd); if (!c) return; /* Fork-free: run in background via system() with & appended. */ size_t n = strlen(c); char* s = malloc(n + 4); if (!s) return; memcpy(s, c, n); memcpy(s + n, " &", 3); system(s); free(s); } /* ── Environment and process ─────────────────────────────────────────────── */ el_val_t __env_get(el_val_t key) { const char* k = EL_CSTR(key); if (!k) return seed_wrap_str(seed_strdup("")); const char* v = getenv(k); return seed_wrap_str(seed_strdup(v ? v : "")); } void __exit_program(el_val_t code) { exit((int)(int64_t)code); } /* ── args_json ────────────────────────────────────────────────────────────── */ static int _seed_argc = 0; static char** _seed_argv = NULL; void el_seed_init_args(int argc, char** argv) { _seed_argc = argc; _seed_argv = argv; } el_val_t __args_json(void) { /* Return ["arg1","arg2",...] as a JSON string. Skip argv[0] (program name). */ size_t cap = 256, len = 0; char* buf = malloc(cap); if (!buf) return seed_wrap_str(seed_strdup("[]")); buf[len++] = '['; int first = 1; for (int i = 1; i < _seed_argc; i++) { const char* a = _seed_argv[i]; /* Estimate: each arg needs at most strlen*6+4 bytes (worst case escape) */ size_t need = strlen(a) * 6 + 8; while (len + need + 2 > cap) { cap *= 2; char* g = realloc(buf, cap); if (!g) { free(buf); return seed_wrap_str(seed_strdup("[]")); } buf = g; } if (!first) buf[len++] = ','; first = 0; buf[len++] = '"'; for (const char* p = a; *p; p++) { unsigned char c = (unsigned char)*p; if (c == '"') { buf[len++] = '\\'; buf[len++] = '"'; } else if (c == '\\') { buf[len++] = '\\'; buf[len++] = '\\'; } else if (c == '\n') { buf[len++] = '\\'; buf[len++] = 'n'; } else if (c == '\r') { buf[len++] = '\\'; buf[len++] = 'r'; } else if (c == '\t') { buf[len++] = '\\'; buf[len++] = 't'; } else buf[len++] = (char)c; } buf[len++] = '"'; } buf[len++] = ']'; buf[len] = '\0'; seed_arena_track(buf); return seed_wrap_str(buf); } /* ── Time ────────────────────────────────────────────────────────────────── */ el_val_t __time_now_ns(void) { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { int64_t ns = (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec; return (el_val_t)ns; } struct timeval tv; gettimeofday(&tv, NULL); return (el_val_t)((int64_t)tv.tv_sec * 1000000000LL + (int64_t)tv.tv_usec * 1000LL); } void __sleep_ms(el_val_t ms) { int64_t m = (int64_t)ms; if (m < 0) m = 0; struct timespec ts; ts.tv_sec = (time_t)(m / 1000LL); ts.tv_nsec = (long)((m % 1000LL) * 1000000LL); nanosleep(&ts, NULL); } /* ── UUID ────────────────────────────────────────────────────────────────── */ static int _seed_uuid_seeded = 0; el_val_t __uuid_v4(void) { if (!_seed_uuid_seeded) { srand((unsigned)time(NULL) ^ (unsigned)(uintptr_t)&_seed_uuid_seeded); _seed_uuid_seeded = 1; } unsigned char b[16]; for (int i = 0; i < 16; i++) b[i] = (unsigned char)(rand() & 0xff); b[6] = (b[6] & 0x0f) | 0x40; /* version 4 */ b[8] = (b[8] & 0x3f) | 0x80; /* RFC 4122 variant */ char buf[37]; snprintf(buf, sizeof(buf), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", b[0],b[1],b[2],b[3], b[4],b[5], b[6],b[7], b[8],b[9], b[10],b[11],b[12],b[13],b[14],b[15]); return seed_wrap_str(seed_strdup(buf)); } /* ── Math ────────────────────────────────────────────────────────────────── */ el_val_t __sqrt_f(el_val_t f) { return el_from_float(sqrt(el_to_float(f))); } el_val_t __log_f(el_val_t f) { return el_from_float(log10(el_to_float(f))); } el_val_t __ln_f(el_val_t f) { return el_from_float(log(el_to_float(f))); } el_val_t __sin_f(el_val_t f) { return el_from_float(sin(el_to_float(f))); } el_val_t __cos_f(el_val_t f) { return el_from_float(cos(el_to_float(f))); } el_val_t __pi_f(void) { return el_from_float(3.14159265358979323846); } /* ── JSON — thin wrappers around el_runtime.c implementations ────────────── */ el_val_t __json_get(el_val_t json, el_val_t key) { return json_get(json, key); } el_val_t __json_get_raw(el_val_t json_str, el_val_t key) { return json_get_raw(json_str, key); } el_val_t __json_parse(el_val_t s) { return json_parse(s); } el_val_t __json_stringify(el_val_t v) { return json_stringify(v); } el_val_t __json_parse_map(el_val_t json_str) { return json_parse(json_str); } el_val_t __json_stringify_val(el_val_t val) { return json_stringify(val); } el_val_t __json_array_len(el_val_t json_str) { return json_array_len(json_str); } el_val_t __json_array_get(el_val_t json_str, el_val_t index) { return json_array_get(json_str, index); } el_val_t __json_array_get_string(el_val_t json_str, el_val_t index) { return json_array_get_string(json_str, index); } el_val_t __json_get_string(el_val_t json_str, el_val_t key) { return json_get_string(json_str, key); } el_val_t __json_get_int(el_val_t json_str, el_val_t key) { return json_get_int(json_str, key); } el_val_t __json_get_float(el_val_t json_str, el_val_t key) { return json_get_float(json_str, key); } el_val_t __json_get_bool(el_val_t json_str, el_val_t key) { return json_get_bool(json_str, key); } el_val_t __json_set(el_val_t json_str, el_val_t key, el_val_t value) { return json_set(json_str, key, value); } /* ── State K/V — thin wrappers ───────────────────────────────────────────── */ el_val_t __state_set(el_val_t key, el_val_t value) { return state_set(key, value); } el_val_t __state_get(el_val_t key) { return state_get(key); } el_val_t __state_del(el_val_t key) { return state_del(key); } el_val_t __state_keys(void) { return state_keys(); } /* ── HTML/URL — thin wrappers ────────────────────────────────────────────── */ el_val_t __html_sanitize(el_val_t input_html, el_val_t allowlist_json) { return el_html_sanitize(input_html, allowlist_json); } el_val_t __url_encode(el_val_t s) { return url_encode(s); } el_val_t __url_decode(el_val_t s) { return url_decode(s); } /* ── Engram — thin wrappers ──────────────────────────────────────────────── */ el_val_t __engram_node(el_val_t content, el_val_t node_type, el_val_t salience) { return engram_node(content, node_type, salience); } el_val_t __engram_node_full(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t importance, el_val_t confidence, el_val_t tier, el_val_t tags) { return engram_node_full(content, node_type, label, salience, importance, confidence, tier, tags); } el_val_t __engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t certainty, el_val_t confidence, el_val_t status, el_val_t tags, el_val_t layer_id) { return engram_node_layered(content, node_type, label, salience, certainty, confidence, status, tags, layer_id); } el_val_t __engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible, el_val_t transparent, el_val_t injectable) { return engram_add_layer(name, priority, suppressible, transparent, injectable); } el_val_t __engram_remove_layer(el_val_t layer_id) { return engram_remove_layer(layer_id); } el_val_t __engram_list_layers(void) { return engram_list_layers(); } el_val_t __engram_get_node(el_val_t id) { return engram_get_node(id); } void __engram_strengthen(el_val_t node_id) { engram_strengthen(node_id); } void __engram_forget(el_val_t node_id) { engram_forget(node_id); } el_val_t __engram_node_count(void) { return engram_node_count(); } el_val_t __engram_search(el_val_t query, el_val_t limit) { return engram_search(query, limit); } el_val_t __engram_scan_nodes(el_val_t limit, el_val_t offset) { return engram_scan_nodes(limit, offset); } void __engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation) { engram_connect(from_id, to_id, weight, relation); } el_val_t __engram_edge_between(el_val_t from_id, el_val_t to_id) { return engram_edge_between(from_id, to_id); } el_val_t __engram_neighbors(el_val_t node_id) { return engram_neighbors(node_id); } el_val_t __engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction) { return engram_neighbors_filtered(node_id, max_depth, direction); } el_val_t __engram_edge_count(void) { return engram_edge_count(); } el_val_t __engram_activate(el_val_t query, el_val_t depth) { return engram_activate(query, depth); } el_val_t __engram_save(el_val_t path) { return engram_save(path); } el_val_t __engram_load(el_val_t path) { return engram_load(path); } el_val_t __engram_get_node_json(el_val_t id) { return engram_get_node_json(id); } el_val_t __engram_search_json(el_val_t query, el_val_t limit) { return engram_search_json(query, limit); } el_val_t __engram_scan_nodes_json(el_val_t limit, el_val_t offset) { return engram_scan_nodes_json(limit, offset); } el_val_t __engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset) { return engram_scan_nodes_by_type_json(node_type, limit, offset); } el_val_t __engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction) { return engram_neighbors_json(node_id, max_depth, direction); } el_val_t __engram_activate_json(el_val_t query, el_val_t depth) { return engram_activate_json(query, depth); } el_val_t __engram_stats_json(void) { return engram_stats_json(); } el_val_t __engram_list_layers_json(void) { return engram_list_layers_json(); } el_val_t __engram_compile_layered_json(el_val_t intent, el_val_t depth) { return engram_compile_layered_json(intent, depth); } /* ── Cryptographic hashing ────────────────────────────────────────────────── */ /* * SHA-256 — self-contained implementation (no OpenSSL dependency). * Based on Brad Conte's public-domain reference implementation. */ typedef struct { uint8_t data[64]; uint32_t datalen; uint64_t bitlen; uint32_t state[8]; } _seed_sha256_ctx; static const uint32_t _seed_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 }; #define _SEED_ROTR32(x,n) (((x)>>(n))|((x)<<(32-(n)))) #define _SEED_CH(x,y,z) (((x)&(y))^(~(x)&(z))) #define _SEED_MAJ(x,y,z) (((x)&(y))^((x)&(z))^((y)&(z))) #define _SEED_EP0(x) (_SEED_ROTR32(x,2)^_SEED_ROTR32(x,13)^_SEED_ROTR32(x,22)) #define _SEED_EP1(x) (_SEED_ROTR32(x,6)^_SEED_ROTR32(x,11)^_SEED_ROTR32(x,25)) #define _SEED_SIG0(x) (_SEED_ROTR32(x,7)^_SEED_ROTR32(x,18)^((x)>>3)) #define _SEED_SIG1(x) (_SEED_ROTR32(x,17)^_SEED_ROTR32(x,19)^((x)>>10)) static void _seed_sha256_transform(_seed_sha256_ctx* ctx, const uint8_t* data) { uint32_t a,b,c,d,e,f,g,h,t1,t2,m[64]; for (int i=0,j=0; i<16; i++,j+=4) m[i]=(uint32_t)(data[j]<<24)|(data[j+1]<<16)|(data[j+2]<<8)|data[j+3]; for (int i=16; i<64; i++) m[i]=_SEED_SIG1(m[i-2])+m[i-7]+_SEED_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 (int i=0; i<64; i++) { t1=h+_SEED_EP1(e)+_SEED_CH(e,f,g)+_seed_sha256_k[i]+m[i]; t2=_SEED_EP0(a)+_SEED_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; } static void _seed_sha256_init(_seed_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; } static void _seed_sha256_update(_seed_sha256_ctx* ctx, const uint8_t* data, size_t len) { for (size_t i=0; idata[ctx->datalen++] = data[i]; if (ctx->datalen==64) { _seed_sha256_transform(ctx,ctx->data); ctx->bitlen+=512; ctx->datalen=0; } } } static void _seed_sha256_final(_seed_sha256_ctx* ctx, uint8_t hash[32]) { uint32_t i=ctx->datalen; ctx->data[i++]=0x80; if (ctx->datalen<56) { while(i<56) ctx->data[i++]=0; } else { while(i<64) ctx->data[i++]=0; _seed_sha256_transform(ctx,ctx->data); memset(ctx->data,0,56); } ctx->bitlen+=ctx->datalen*8; ctx->data[63]=(uint8_t)(ctx->bitlen); ctx->data[62]=(uint8_t)(ctx->bitlen>>8); ctx->data[61]=(uint8_t)(ctx->bitlen>>16); ctx->data[60]=(uint8_t)(ctx->bitlen>>24); ctx->data[59]=(uint8_t)(ctx->bitlen>>32); ctx->data[58]=(uint8_t)(ctx->bitlen>>40); ctx->data[57]=(uint8_t)(ctx->bitlen>>48); ctx->data[56]=(uint8_t)(ctx->bitlen>>56); _seed_sha256_transform(ctx,ctx->data); for (i=0; i<4; i++) { hash[i] =(uint8_t)(ctx->state[0]>>(24-i*8)); hash[i+4] =(uint8_t)(ctx->state[1]>>(24-i*8)); hash[i+8] =(uint8_t)(ctx->state[2]>>(24-i*8)); hash[i+12] =(uint8_t)(ctx->state[3]>>(24-i*8)); hash[i+16] =(uint8_t)(ctx->state[4]>>(24-i*8)); hash[i+20] =(uint8_t)(ctx->state[5]>>(24-i*8)); hash[i+24] =(uint8_t)(ctx->state[6]>>(24-i*8)); hash[i+28] =(uint8_t)(ctx->state[7]>>(24-i*8)); } } /* ── Manifest reader (shared: macOS + Linux) ─────────────────────────────── */ /* * Parse the app{} block from a manifest.el file. * Expected format (subset — only app{} block is parsed): * * app { * window_title "My App" * window_width 1200 * window_height 800 * min_width 600 * min_height 400 * } * * Returns JSON: {"title":"...","width":N,"height":N,"min_width":N,"min_height":N} * Returns "{}" on parse failure. * * Compiled unconditionally so both EL_TARGET_MACOS and EL_TARGET_LINUX can use * it — the __manifest_read builtin is identical on all native targets. */ static void seed_manifest_skip_ws(const char** p) { while (**p && (unsigned char)**p <= ' ') (*p)++; } /* Read a quoted string literal. Caller must free() the result. */ static char* seed_manifest_read_string(const char** p) { seed_manifest_skip_ws(p); if (**p != '"') return NULL; (*p)++; const char* start = *p; while (**p && **p != '"') { if (**p == '\\') (*p)++; /* skip escaped char */ (*p)++; } size_t len = (size_t)(*p - start); if (**p == '"') (*p)++; char* out = malloc(len + 1); if (!out) return NULL; memcpy(out, start, len); out[len] = '\0'; return out; } /* Read an unquoted integer literal. */ static int64_t seed_manifest_read_int(const char** p) { seed_manifest_skip_ws(p); int64_t result = 0; int neg = 0; if (**p == '-') { neg = 1; (*p)++; } while (**p >= '0' && **p <= '9') { result = result * 10 + (**p - '0'); (*p)++; } return neg ? -result : result; } /* Find "app {" block start. Returns pointer past the '{'. */ static const char* seed_manifest_find_app_block(const char* src) { while (*src) { /* Skip line comments (// ...) */ if (src[0] == '/' && src[1] == '/') { while (*src && *src != '\n') src++; continue; } if (strncmp(src, "app", 3) == 0 && (src[3] == ' ' || src[3] == '\t' || src[3] == '\n' || src[3] == '{')) { src += 3; while (*src && *src != '{') src++; if (*src == '{') return src + 1; return NULL; } src++; } return NULL; } el_val_t __manifest_read(el_val_t path) { const char* p = EL_CSTR(path); if (!p || !*p) return seed_wrap_str(seed_strdup("{}")); /* Read the file. */ FILE* f = fopen(p, "r"); if (!f) return seed_wrap_str(seed_strdup("{}")); fseek(f, 0, SEEK_END); long sz = ftell(f); rewind(f); if (sz <= 0) { fclose(f); return seed_wrap_str(seed_strdup("{}")); } char* src = malloc((size_t)sz + 1); if (!src) { fclose(f); return seed_wrap_str(seed_strdup("{}")); } size_t got = fread(src, 1, (size_t)sz, f); src[got] = '\0'; fclose(f); const char* app_start = seed_manifest_find_app_block(src); if (!app_start) { free(src); return seed_wrap_str(seed_strdup("{}")); } /* Parse key-value pairs inside the app { } block. */ char* title = NULL; int64_t width = 1200; int64_t height = 800; int64_t min_w = 600; int64_t min_h = 400; const char* cur = app_start; while (*cur && *cur != '}') { /* Skip whitespace and comments. */ while (*cur && (unsigned char)*cur <= ' ') cur++; if (*cur == '}') break; if (cur[0] == '/' && cur[1] == '/') { while (*cur && *cur != '\n') cur++; continue; } /* Read key token. */ const char* key_start = cur; while (*cur && (unsigned char)*cur > ' ' && *cur != '{' && *cur != '}') cur++; size_t key_len = (size_t)(cur - key_start); if (key_len == 0) { cur++; continue; } /* Skip whitespace between key and value. */ while (*cur == ' ' || *cur == '\t') cur++; if (strncmp(key_start, "window_title", key_len) == 0 && key_len == 12) { free(title); title = seed_manifest_read_string(&cur); } else if (strncmp(key_start, "window_width", key_len) == 0 && key_len == 12) { width = seed_manifest_read_int(&cur); } else if (strncmp(key_start, "window_height", key_len) == 0 && key_len == 13) { height = seed_manifest_read_int(&cur); } else if (strncmp(key_start, "min_width", key_len) == 0 && key_len == 9) { min_w = seed_manifest_read_int(&cur); } else if (strncmp(key_start, "min_height", key_len) == 0 && key_len == 10) { min_h = seed_manifest_read_int(&cur); } else { /* Unknown key — skip to end of line. */ while (*cur && *cur != '\n' && *cur != '}') cur++; } } free(src); /* Build JSON result. */ const char* t = title ? title : "App"; size_t buf_sz = strlen(t) * 6 + 128; char* buf = seed_strbuf(buf_sz); snprintf(buf, buf_sz, "{\"title\":\"%s\",\"width\":%lld,\"height\":%lld,\"min_width\":%lld,\"min_height\":%lld}", t, (long long)width, (long long)height, (long long)min_w, (long long)min_h); free(title); return seed_wrap_str(buf); } /* ── Native widget system — macOS AppKit ─────────────────────────────────── */ /* * These builtins are compiled in only when EL_TARGET_MACOS is defined. * They call through to el_appkit.m (Objective-C) which implements the actual * AppKit widget creation and management. * * Each builtin is a thin wrapper: marshal el_val_t ↔ C types, call the * el_appkit_* function, return the result. * * Compile requirements: * - Compile el_appkit.m alongside this file (clang -ObjC -framework Cocoa) * - Pass -DEL_TARGET_MACOS to enable this section */ #ifdef EL_TARGET_MACOS /* Forward declarations — implemented in el_appkit.m */ extern void el_appkit_init(void); extern void el_appkit_run_loop(void); extern int64_t el_appkit_window_create(const char* title, int w, int h, int mw, int mh); extern void el_appkit_window_show(int64_t handle); extern void el_appkit_window_set_title(int64_t handle, const char* title); extern int64_t el_appkit_vstack_create(int spacing); extern int64_t el_appkit_hstack_create(int spacing); extern int64_t el_appkit_zstack_create(void); extern int64_t el_appkit_scroll_create(void); extern int64_t el_appkit_label_create(const char* text); extern int64_t el_appkit_button_create(const char* label); extern int64_t el_appkit_text_field_create(const char* placeholder); extern int64_t el_appkit_text_area_create(const char* placeholder); extern int64_t el_appkit_image_create(const char* path_or_name); extern void el_appkit_widget_set_text(int64_t handle, const char* text); extern const char* el_appkit_widget_get_text(int64_t handle); extern void el_appkit_widget_set_color(int64_t h, float r, float g, float b, float a); extern void el_appkit_widget_set_bg_color(int64_t h, float r, float g, float b, float a); extern void el_appkit_widget_set_font(int64_t h, const char* family, int size, int bold); extern void el_appkit_widget_set_padding(int64_t h, int top, int right, int bottom, int left); extern void el_appkit_widget_set_width(int64_t h, int width); extern void el_appkit_widget_set_height(int64_t h, int height); extern void el_appkit_widget_set_flex(int64_t h, int flex); extern void el_appkit_widget_set_corner_radius(int64_t h, int radius); extern void el_appkit_widget_set_disabled(int64_t h, int disabled); extern void el_appkit_widget_set_hidden(int64_t h, int hidden); extern void el_appkit_widget_add_child(int64_t parent, int64_t child); extern void el_appkit_widget_remove_child(int64_t parent, int64_t child); extern void el_appkit_widget_destroy(int64_t handle); extern void el_appkit_widget_on_click(int64_t h, const char* fn_name); extern void el_appkit_widget_on_change(int64_t h, const char* fn_name); extern void el_appkit_widget_on_submit(int64_t h, const char* fn_name); /* ── Native widget builtins ──────────────────────────────────────────────── */ void __native_init(void) { el_appkit_init(); } void __native_run_loop(void) { el_appkit_run_loop(); } el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) { return (el_val_t)el_appkit_window_create( EL_CSTR(title), (int)(int64_t)width, (int)(int64_t)height, (int)(int64_t)min_width, (int)(int64_t)min_height); } void __window_show(el_val_t h) { el_appkit_window_show((int64_t)h); } void __window_set_title(el_val_t h, el_val_t t) { el_appkit_window_set_title((int64_t)h, EL_CSTR(t)); } el_val_t __vstack_create(el_val_t spacing) { return (el_val_t)el_appkit_vstack_create((int)(int64_t)spacing); } el_val_t __hstack_create(el_val_t spacing) { return (el_val_t)el_appkit_hstack_create((int)(int64_t)spacing); } el_val_t __zstack_create(void) { return (el_val_t)el_appkit_zstack_create(); } el_val_t __scroll_create(void) { return (el_val_t)el_appkit_scroll_create(); } el_val_t __label_create(el_val_t text) { return (el_val_t)el_appkit_label_create(EL_CSTR(text)); } el_val_t __button_create(el_val_t label) { return (el_val_t)el_appkit_button_create(EL_CSTR(label)); } el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_appkit_text_field_create(EL_CSTR(placeholder)); } el_val_t __text_area_create(el_val_t placeholder) { return (el_val_t)el_appkit_text_area_create(EL_CSTR(placeholder)); } el_val_t __image_create(el_val_t path_or_name) { return (el_val_t)el_appkit_image_create(EL_CSTR(path_or_name)); } void __widget_set_text(el_val_t h, el_val_t t) { el_appkit_widget_set_text((int64_t)h, EL_CSTR(t)); } el_val_t __widget_get_text(el_val_t h) { const char* s = el_appkit_widget_get_text((int64_t)h); if (!s) return seed_wrap_str(seed_strdup("")); /* el_appkit returns strdup'd string — wrap it (arena won't track it, but * caller must treat return as short-lived or copy it). */ return EL_STR(s); } void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_appkit_widget_set_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_appkit_widget_set_bg_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) { el_appkit_widget_set_font((int64_t)h, EL_CSTR(family), (int)(int64_t)size, (int)(int64_t)bold); } void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) { el_appkit_widget_set_padding((int64_t)h, (int)(int64_t)top, (int)(int64_t)right, (int)(int64_t)bottom, (int)(int64_t)left); } void __widget_set_width(el_val_t h, el_val_t w) { el_appkit_widget_set_width((int64_t)h, (int)(int64_t)w); } void __widget_set_height(el_val_t h, el_val_t ht) { el_appkit_widget_set_height((int64_t)h, (int)(int64_t)ht); } void __widget_set_flex(el_val_t h, el_val_t f) { el_appkit_widget_set_flex((int64_t)h, (int)(int64_t)f); } void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_appkit_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); } void __widget_set_disabled(el_val_t h, el_val_t d) { el_appkit_widget_set_disabled((int64_t)h, (int)(int64_t)d); } void __widget_set_hidden(el_val_t h, el_val_t hid) { el_appkit_widget_set_hidden((int64_t)h, (int)(int64_t)hid); } void __widget_add_child(el_val_t p, el_val_t c) { el_appkit_widget_add_child((int64_t)p, (int64_t)c); } void __widget_remove_child(el_val_t p, el_val_t c) { el_appkit_widget_remove_child((int64_t)p, (int64_t)c); } void __widget_destroy(el_val_t h) { el_appkit_widget_destroy((int64_t)h); } void __widget_on_click(el_val_t h, el_val_t fn_name) { el_appkit_widget_on_click((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_change(el_val_t h, el_val_t fn_name) { el_appkit_widget_on_change((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_submit(el_val_t h, el_val_t fn_name) { el_appkit_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); } #endif /* EL_TARGET_MACOS */ /* ── Native widget system — Linux GTK4 ──────────────────────────────────── */ /* * These builtins are compiled in only when EL_TARGET_LINUX is defined. * They call through to el_gtk4.c which implements the GTK4 widget layer. * * Each builtin is a thin wrapper: marshal el_val_t ↔ C types, call the * el_gtk4_* function, return the result. * * Compile requirements: * - Compile el_gtk4.c alongside this file * gcc -DEL_TARGET_LINUX $(pkg-config --cflags gtk4) el_gtk4.c -c ... * - Pass -DEL_TARGET_LINUX to enable this section * - Link with $(pkg-config --libs gtk4) -ldl * * __manifest_read is shared with the macOS path (implemented above in the * seed_manifest_* helpers) and compiled for both targets unconditionally. */ #ifdef EL_TARGET_LINUX /* Forward declarations — implemented in el_gtk4.c */ extern void el_gtk4_init(void); extern void el_gtk4_run_loop(void); extern int64_t el_gtk4_window_create(const char* title, int w, int h, int mw, int mh); extern void el_gtk4_window_show(int64_t handle); extern void el_gtk4_window_set_title(int64_t handle, const char* title); extern int64_t el_gtk4_vstack_create(int spacing); extern int64_t el_gtk4_hstack_create(int spacing); extern int64_t el_gtk4_zstack_create(void); extern int64_t el_gtk4_scroll_create(void); extern int64_t el_gtk4_label_create(const char* text); extern int64_t el_gtk4_button_create(const char* label); extern int64_t el_gtk4_text_field_create(const char* placeholder); extern int64_t el_gtk4_text_area_create(const char* placeholder); extern int64_t el_gtk4_image_create(const char* path_or_name); extern void el_gtk4_widget_set_text(int64_t handle, const char* text); extern const char* el_gtk4_widget_get_text(int64_t handle); extern void el_gtk4_widget_set_color(int64_t h, float r, float g, float b, float a); extern void el_gtk4_widget_set_bg_color(int64_t h, float r, float g, float b, float a); extern void el_gtk4_widget_set_font(int64_t h, const char* family, int size, int bold); extern void el_gtk4_widget_set_padding(int64_t h, int top, int right, int bottom, int left); extern void el_gtk4_widget_set_width(int64_t h, int width); extern void el_gtk4_widget_set_height(int64_t h, int height); extern void el_gtk4_widget_set_flex(int64_t h, int flex); extern void el_gtk4_widget_set_corner_radius(int64_t h, int radius); extern void el_gtk4_widget_set_disabled(int64_t h, int disabled); extern void el_gtk4_widget_set_hidden(int64_t h, int hidden); extern void el_gtk4_widget_add_child(int64_t parent, int64_t child); extern void el_gtk4_widget_remove_child(int64_t parent, int64_t child); extern void el_gtk4_widget_destroy(int64_t handle); extern void el_gtk4_widget_on_click(int64_t h, const char* fn_name); extern void el_gtk4_widget_on_change(int64_t h, const char* fn_name); extern void el_gtk4_widget_on_submit(int64_t h, const char* fn_name); void __native_init(void) { el_gtk4_init(); } void __native_run_loop(void) { el_gtk4_run_loop(); } el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) { return (el_val_t)el_gtk4_window_create( EL_CSTR(title), (int)(int64_t)width, (int)(int64_t)height, (int)(int64_t)min_width, (int)(int64_t)min_height); } void __window_show(el_val_t h) { el_gtk4_window_show((int64_t)h); } void __window_set_title(el_val_t h, el_val_t t) { el_gtk4_window_set_title((int64_t)h, EL_CSTR(t)); } el_val_t __vstack_create(el_val_t spacing) { return (el_val_t)el_gtk4_vstack_create((int)(int64_t)spacing); } el_val_t __hstack_create(el_val_t spacing) { return (el_val_t)el_gtk4_hstack_create((int)(int64_t)spacing); } el_val_t __zstack_create(void) { return (el_val_t)el_gtk4_zstack_create(); } el_val_t __scroll_create(void) { return (el_val_t)el_gtk4_scroll_create(); } el_val_t __label_create(el_val_t text) { return (el_val_t)el_gtk4_label_create(EL_CSTR(text)); } el_val_t __button_create(el_val_t label) { return (el_val_t)el_gtk4_button_create(EL_CSTR(label)); } el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_gtk4_text_field_create(EL_CSTR(placeholder)); } el_val_t __text_area_create(el_val_t placeholder) { return (el_val_t)el_gtk4_text_area_create(EL_CSTR(placeholder)); } el_val_t __image_create(el_val_t path_or_name) { return (el_val_t)el_gtk4_image_create(EL_CSTR(path_or_name)); } void __widget_set_text(el_val_t h, el_val_t t) { el_gtk4_widget_set_text((int64_t)h, EL_CSTR(t)); } el_val_t __widget_get_text(el_val_t h) { const char* s = el_gtk4_widget_get_text((int64_t)h); if (!s) return EL_STR(""); return EL_STR(s); /* el_gtk4 returns strdup'd string */ } void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_gtk4_widget_set_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_gtk4_widget_set_bg_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) { el_gtk4_widget_set_font((int64_t)h, EL_CSTR(family), (int)(int64_t)size, (int)(int64_t)bold); } void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) { el_gtk4_widget_set_padding((int64_t)h, (int)(int64_t)top, (int)(int64_t)right, (int)(int64_t)bottom, (int)(int64_t)left); } void __widget_set_width(el_val_t h, el_val_t w) { el_gtk4_widget_set_width((int64_t)h, (int)(int64_t)w); } void __widget_set_height(el_val_t h, el_val_t ht) { el_gtk4_widget_set_height((int64_t)h, (int)(int64_t)ht); } void __widget_set_flex(el_val_t h, el_val_t f) { el_gtk4_widget_set_flex((int64_t)h, (int)(int64_t)f); } void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_gtk4_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); } void __widget_set_disabled(el_val_t h, el_val_t d) { el_gtk4_widget_set_disabled((int64_t)h, (int)(int64_t)d); } void __widget_set_hidden(el_val_t h, el_val_t hid) { el_gtk4_widget_set_hidden((int64_t)h, (int)(int64_t)hid); } void __widget_add_child(el_val_t p, el_val_t c) { el_gtk4_widget_add_child((int64_t)p, (int64_t)c); } void __widget_remove_child(el_val_t p, el_val_t c) { el_gtk4_widget_remove_child((int64_t)p, (int64_t)c); } void __widget_destroy(el_val_t h) { el_gtk4_widget_destroy((int64_t)h); } void __widget_on_click(el_val_t h, el_val_t fn_name) { el_gtk4_widget_on_click((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_change(el_val_t h, el_val_t fn_name) { el_gtk4_widget_on_change((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_submit(el_val_t h, el_val_t fn_name) { el_gtk4_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); } #endif /* EL_TARGET_LINUX */ /* ── Native widget system — SDL2 (embedded / Pi) ─────────────────────────── */ /* * These builtins are compiled in only when EL_TARGET_SDL2 is defined. * They call through to el_sdl2.c which implements a retained-mode pixel * widget layer on top of SDL2 + SDL_ttf + SDL_image. * * Each builtin is a thin wrapper: marshal el_val_t ↔ C types, call the * el_sdl2_* function, return the result. * * Compile requirements: * gcc -DEL_TARGET_SDL2 $(sdl2-config --cflags) el_sdl2.c -c ... * Pass -DEL_TARGET_SDL2 to enable this section. * Link: * $(sdl2-config --libs) -lSDL2_ttf -lSDL2_image -ldl * * __manifest_read is shared with the macOS/Linux paths (implemented above). */ #ifdef EL_TARGET_SDL2 /* Forward declarations — implemented in el_sdl2.c */ extern void el_sdl2_init(void); extern void el_sdl2_run_loop(void); extern int64_t el_sdl2_window_create(const char* title, int w, int h, int mw, int mh); extern void el_sdl2_window_show(int64_t handle); extern void el_sdl2_window_set_title(int64_t handle, const char* title); extern int64_t el_sdl2_vstack_create(int spacing); extern int64_t el_sdl2_hstack_create(int spacing); extern int64_t el_sdl2_zstack_create(void); extern int64_t el_sdl2_scroll_create(void); extern int64_t el_sdl2_label_create(const char* text); extern int64_t el_sdl2_button_create(const char* label); extern int64_t el_sdl2_text_field_create(const char* placeholder); extern int64_t el_sdl2_text_area_create(const char* placeholder); extern int64_t el_sdl2_image_create(const char* path_or_name); extern void el_sdl2_widget_set_text(int64_t handle, const char* text); extern const char* el_sdl2_widget_get_text(int64_t handle); extern void el_sdl2_widget_set_color(int64_t h, float r, float g, float b, float a); extern void el_sdl2_widget_set_bg_color(int64_t h, float r, float g, float b, float a); extern void el_sdl2_widget_set_font(int64_t h, const char* family, int size, int bold); extern void el_sdl2_widget_set_padding(int64_t h, int top, int right, int bottom, int left); extern void el_sdl2_widget_set_width(int64_t h, int width); extern void el_sdl2_widget_set_height(int64_t h, int height); extern void el_sdl2_widget_set_flex(int64_t h, int flex); extern void el_sdl2_widget_set_corner_radius(int64_t h, int radius); extern void el_sdl2_widget_set_disabled(int64_t h, int disabled); extern void el_sdl2_widget_set_hidden(int64_t h, int hidden); extern void el_sdl2_widget_add_child(int64_t parent, int64_t child); extern void el_sdl2_widget_remove_child(int64_t parent, int64_t child); extern void el_sdl2_widget_destroy(int64_t handle); extern void el_sdl2_widget_on_click(int64_t h, const char* fn_name); extern void el_sdl2_widget_on_change(int64_t h, const char* fn_name); extern void el_sdl2_widget_on_submit(int64_t h, const char* fn_name); void __native_init(void) { el_sdl2_init(); } void __native_run_loop(void) { el_sdl2_run_loop(); } el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) { return (el_val_t)el_sdl2_window_create( EL_CSTR(title), (int)(int64_t)width, (int)(int64_t)height, (int)(int64_t)min_width, (int)(int64_t)min_height); } void __window_show(el_val_t h) { el_sdl2_window_show((int64_t)h); } void __window_set_title(el_val_t h, el_val_t t) { el_sdl2_window_set_title((int64_t)h, EL_CSTR(t)); } el_val_t __vstack_create(el_val_t spacing) { return (el_val_t)el_sdl2_vstack_create((int)(int64_t)spacing); } el_val_t __hstack_create(el_val_t spacing) { return (el_val_t)el_sdl2_hstack_create((int)(int64_t)spacing); } el_val_t __zstack_create(void) { return (el_val_t)el_sdl2_zstack_create(); } el_val_t __scroll_create(void) { return (el_val_t)el_sdl2_scroll_create(); } el_val_t __label_create(el_val_t text) { return (el_val_t)el_sdl2_label_create(EL_CSTR(text)); } el_val_t __button_create(el_val_t label) { return (el_val_t)el_sdl2_button_create(EL_CSTR(label)); } el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_sdl2_text_field_create(EL_CSTR(placeholder)); } el_val_t __text_area_create(el_val_t placeholder) { return (el_val_t)el_sdl2_text_area_create(EL_CSTR(placeholder)); } el_val_t __image_create(el_val_t path_or_name) { return (el_val_t)el_sdl2_image_create(EL_CSTR(path_or_name)); } void __widget_set_text(el_val_t h, el_val_t t) { el_sdl2_widget_set_text((int64_t)h, EL_CSTR(t)); } el_val_t __widget_get_text(el_val_t h) { const char* s = el_sdl2_widget_get_text((int64_t)h); if (!s) return EL_STR(""); return EL_STR(s); } void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_sdl2_widget_set_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_sdl2_widget_set_bg_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) { el_sdl2_widget_set_font((int64_t)h, EL_CSTR(family), (int)(int64_t)size, (int)(int64_t)bold); } void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) { el_sdl2_widget_set_padding((int64_t)h, (int)(int64_t)top, (int)(int64_t)right, (int)(int64_t)bottom, (int)(int64_t)left); } void __widget_set_width(el_val_t h, el_val_t w) { el_sdl2_widget_set_width((int64_t)h, (int)(int64_t)w); } void __widget_set_height(el_val_t h, el_val_t ht) { el_sdl2_widget_set_height((int64_t)h, (int)(int64_t)ht); } void __widget_set_flex(el_val_t h, el_val_t f) { el_sdl2_widget_set_flex((int64_t)h, (int)(int64_t)f); } void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_sdl2_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); } void __widget_set_disabled(el_val_t h, el_val_t d) { el_sdl2_widget_set_disabled((int64_t)h, (int)(int64_t)d); } void __widget_set_hidden(el_val_t h, el_val_t hid) { el_sdl2_widget_set_hidden((int64_t)h, (int)(int64_t)hid); } void __widget_add_child(el_val_t p, el_val_t c) { el_sdl2_widget_add_child((int64_t)p, (int64_t)c); } void __widget_remove_child(el_val_t p, el_val_t c) { el_sdl2_widget_remove_child((int64_t)p, (int64_t)c); } void __widget_destroy(el_val_t h) { el_sdl2_widget_destroy((int64_t)h); } void __widget_on_click(el_val_t h, el_val_t fn_name) { el_sdl2_widget_on_click((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_change(el_val_t h, el_val_t fn_name) { el_sdl2_widget_on_change((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_submit(el_val_t h, el_val_t fn_name) { el_sdl2_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); } #endif /* EL_TARGET_SDL2 */ /* ── EL_TARGET_LVGL — LVGL v9 embedded backend ─────────────────────────────── * * These builtins are compiled in only when EL_TARGET_LVGL is defined. * Each wrapper translates el_val_t arguments (opaque int64_t) into the typed * parameters that el_lvgl_* expects, then calls through. * * Build: * gcc -DEL_TARGET_LVGL -I./lvgl el_lvgl.c el_seed.c -c ... * # Link with lvgl.a. For bare-metal add -DEL_LVGL_NO_DLSYM. * * __manifest_read is shared — defined unconditionally above. */ #ifdef EL_TARGET_LVGL extern void el_lvgl_init(void); extern void el_lvgl_run_loop(void); extern int64_t el_lvgl_window_create(const char *title, int w, int h, int mw, int mh); extern void el_lvgl_window_show(int64_t handle); extern void el_lvgl_window_set_title(int64_t handle, const char *title); extern int64_t el_lvgl_vstack_create(int spacing); extern int64_t el_lvgl_hstack_create(int spacing); extern int64_t el_lvgl_zstack_create(void); extern int64_t el_lvgl_scroll_create(void); extern int64_t el_lvgl_label_create(const char *text); extern int64_t el_lvgl_button_create(const char *label); extern int64_t el_lvgl_text_field_create(const char *placeholder); extern int64_t el_lvgl_text_area_create(const char *placeholder); extern int64_t el_lvgl_image_create(const char *path_or_name); extern void el_lvgl_widget_set_text(int64_t handle, const char *text); extern const char *el_lvgl_widget_get_text(int64_t handle); extern void el_lvgl_widget_set_color(int64_t h, float r, float g, float b, float a); extern void el_lvgl_widget_set_bg_color(int64_t h, float r, float g, float b, float a); extern void el_lvgl_widget_set_font(int64_t h, const char *family, int size, int bold); extern void el_lvgl_widget_set_padding(int64_t h, int top, int right, int bottom, int left); extern void el_lvgl_widget_set_width(int64_t h, int width); extern void el_lvgl_widget_set_height(int64_t h, int height); extern void el_lvgl_widget_set_flex(int64_t h, int flex); extern void el_lvgl_widget_set_corner_radius(int64_t h, int radius); extern void el_lvgl_widget_set_disabled(int64_t h, int disabled); extern void el_lvgl_widget_set_hidden(int64_t h, int hidden); extern void el_lvgl_widget_add_child(int64_t parent, int64_t child); extern void el_lvgl_widget_remove_child(int64_t parent, int64_t child); extern void el_lvgl_widget_destroy(int64_t handle); extern void el_lvgl_widget_on_click(int64_t h, const char *fn_name); extern void el_lvgl_widget_on_change(int64_t h, const char *fn_name); extern void el_lvgl_widget_on_submit(int64_t h, const char *fn_name); void __native_init(void) { el_lvgl_init(); } void __native_run_loop(void) { el_lvgl_run_loop(); } el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) { return (el_val_t)el_lvgl_window_create( EL_CSTR(title), (int)(int64_t)width, (int)(int64_t)height, (int)(int64_t)min_width, (int)(int64_t)min_height); } void __window_show(el_val_t h) { el_lvgl_window_show((int64_t)h); } void __window_set_title(el_val_t h, el_val_t t) { el_lvgl_window_set_title((int64_t)h, EL_CSTR(t)); } el_val_t __vstack_create(el_val_t spacing) { return (el_val_t)el_lvgl_vstack_create((int)(int64_t)spacing); } el_val_t __hstack_create(el_val_t spacing) { return (el_val_t)el_lvgl_hstack_create((int)(int64_t)spacing); } el_val_t __zstack_create(void) { return (el_val_t)el_lvgl_zstack_create(); } el_val_t __scroll_create(void) { return (el_val_t)el_lvgl_scroll_create(); } el_val_t __label_create(el_val_t text) { return (el_val_t)el_lvgl_label_create(EL_CSTR(text)); } el_val_t __button_create(el_val_t label) { return (el_val_t)el_lvgl_button_create(EL_CSTR(label)); } el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_lvgl_text_field_create(EL_CSTR(placeholder)); } el_val_t __text_area_create(el_val_t placeholder) { return (el_val_t)el_lvgl_text_area_create(EL_CSTR(placeholder)); } el_val_t __image_create(el_val_t path_or_name) { return (el_val_t)el_lvgl_image_create(EL_CSTR(path_or_name)); } void __widget_set_text(el_val_t h, el_val_t t) { el_lvgl_widget_set_text((int64_t)h, EL_CSTR(t)); } el_val_t __widget_get_text(el_val_t h) { const char *s = el_lvgl_widget_get_text((int64_t)h); if (!s) return EL_STR(""); return EL_STR(s); /* pointer into LVGL internal storage — valid until next LVGL op */ } void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_lvgl_widget_set_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_lvgl_widget_set_bg_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) { el_lvgl_widget_set_font((int64_t)h, EL_CSTR(family), (int)(int64_t)size, (int)(int64_t)bold); } void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) { el_lvgl_widget_set_padding((int64_t)h, (int)(int64_t)top, (int)(int64_t)right, (int)(int64_t)bottom, (int)(int64_t)left); } void __widget_set_width(el_val_t h, el_val_t w) { el_lvgl_widget_set_width((int64_t)h, (int)(int64_t)w); } void __widget_set_height(el_val_t h, el_val_t ht) { el_lvgl_widget_set_height((int64_t)h, (int)(int64_t)ht); } void __widget_set_flex(el_val_t h, el_val_t f) { el_lvgl_widget_set_flex((int64_t)h, (int)(int64_t)f); } void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_lvgl_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); } void __widget_set_disabled(el_val_t h, el_val_t d) { el_lvgl_widget_set_disabled((int64_t)h, (int)(int64_t)d); } void __widget_set_hidden(el_val_t h, el_val_t hid) { el_lvgl_widget_set_hidden((int64_t)h, (int)(int64_t)hid); } void __widget_add_child(el_val_t p, el_val_t c) { el_lvgl_widget_add_child((int64_t)p, (int64_t)c); } void __widget_remove_child(el_val_t p, el_val_t c) { el_lvgl_widget_remove_child((int64_t)p, (int64_t)c); } void __widget_destroy(el_val_t h) { el_lvgl_widget_destroy((int64_t)h); } void __widget_on_click(el_val_t h, el_val_t fn_name) { el_lvgl_widget_on_click((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_change(el_val_t h, el_val_t fn_name) { el_lvgl_widget_on_change((int64_t)h, EL_CSTR(fn_name)); } void __widget_on_submit(el_val_t h, el_val_t fn_name) { el_lvgl_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); } #endif /* EL_TARGET_LVGL */ /* ── Cryptographic hashing ────────────────────────────────────────────────── */ el_val_t __sha256_hex(el_val_t sv) { const char* s = EL_CSTR(sv); if (!s) s = ""; _seed_sha256_ctx ctx; _seed_sha256_init(&ctx); _seed_sha256_update(&ctx, (const uint8_t*)s, strlen(s)); uint8_t digest[32]; _seed_sha256_final(&ctx, digest); static const char hex[] = "0123456789abcdef"; char* out = malloc(65); if (!out) return EL_STR(""); for (int i=0; i<32; i++) { out[i*2] = hex[(digest[i]>>4)&0xf]; out[i*2+1] = hex[digest[i]&0xf]; } out[64] = '\0'; return EL_STR(out); }