/* * el_runtime.c — El language C runtime implementation * * All functions use el_val_t (= int64_t) as the universal value type. * Strings are transported as their pointer address cast to int64_t. * On any 64-bit system sizeof(pointer) <= sizeof(int64_t), so this is safe. * * Compile with: * cc -std=c11 -I -o .c el_runtime.c */ #include "el_runtime.h" #include #include #include #include #include #include /* ── Internal allocators ─────────────────────────────────────────────────── */ static char* el_strdup(const char* s) { if (!s) return strdup(""); return strdup(s); } static char* el_strbuf(size_t n) { char* p = malloc(n + 1); if (!p) { fputs("el_runtime: out of memory\n", stderr); exit(1); } p[0] = '\0'; return p; } /* Wrap an allocated C string as el_val_t */ static el_val_t el_wrap_str(char* s) { return EL_STR(s); } /* ── I/O ──────────────────────────────────────────────────────────────────── */ void println(el_val_t s) { const char* str = EL_CSTR(s); if (str) puts(str); else puts(""); } void print(el_val_t s) { const char* str = EL_CSTR(s); if (str) fputs(str, stdout); } el_val_t readline(void) { char buf[4096]; if (!fgets(buf, sizeof(buf), stdin)) return el_wrap_str(el_strdup("")); size_t len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; return el_wrap_str(el_strdup(buf)); } /* ── String builtins ─────────────────────────────────────────────────────── */ el_val_t el_str_concat(el_val_t av, el_val_t bv) { const char* a = EL_CSTR(av); const char* b = EL_CSTR(bv); if (!a) a = ""; if (!b) b = ""; size_t la = strlen(a); size_t lb = strlen(b); char* out = el_strbuf(la + lb); memcpy(out, a, la); memcpy(out + la, b, lb); out[la + lb] = '\0'; return el_wrap_str(out); } el_val_t str_eq(el_val_t av, el_val_t bv) { const char* a = EL_CSTR(av); const char* b = EL_CSTR(bv); if (!a || !b) return (el_val_t)(a == b); return (el_val_t)(strcmp(a, b) == 0); } el_val_t str_starts_with(el_val_t sv, el_val_t prefv) { const char* s = EL_CSTR(sv); const char* prefix = EL_CSTR(prefv); if (!s || !prefix) return 0; size_t lp = strlen(prefix); return (el_val_t)(strncmp(s, prefix, lp) == 0); } el_val_t str_ends_with(el_val_t sv, el_val_t sufv) { const char* s = EL_CSTR(sv); const char* suffix = EL_CSTR(sufv); if (!s || !suffix) return 0; size_t ls = strlen(s); size_t lsuf = strlen(suffix); if (lsuf > ls) return 0; return (el_val_t)(strcmp(s + ls - lsuf, suffix) == 0); } el_val_t str_len(el_val_t sv) { const char* s = EL_CSTR(sv); if (!s) return 0; return (el_val_t)strlen(s); } el_val_t str_concat(el_val_t a, el_val_t b) { return el_str_concat(a, b); } el_val_t int_to_str(el_val_t n) { char buf[32]; snprintf(buf, sizeof(buf), "%lld", (long long)n); return el_wrap_str(el_strdup(buf)); } el_val_t str_to_int(el_val_t sv) { const char* s = EL_CSTR(sv); if (!s) return 0; return (el_val_t)atoll(s); } el_val_t str_slice(el_val_t sv, el_val_t start, el_val_t end) { const char* s = EL_CSTR(sv); if (!s) return el_wrap_str(el_strdup("")); int64_t len = (int64_t)strlen(s); if (start < 0) start = 0; if (end > len) end = len; if (start >= end) return el_wrap_str(el_strdup("")); int64_t sz = end - start; char* out = el_strbuf((size_t)sz); memcpy(out, s + start, (size_t)sz); out[sz] = '\0'; return el_wrap_str(out); } el_val_t str_contains(el_val_t sv, el_val_t subv) { const char* s = EL_CSTR(sv); const char* sub = EL_CSTR(subv); if (!s || !sub) return 0; return (el_val_t)(strstr(s, sub) != NULL); } el_val_t str_replace(el_val_t sv, el_val_t fromv, el_val_t tov) { const char* s = EL_CSTR(sv); const char* from = EL_CSTR(fromv); const char* to = EL_CSTR(tov); if (!s || !from || !to) return el_wrap_str(el_strdup(s ? s : "")); size_t ls = strlen(s); size_t lf = strlen(from); size_t lt = strlen(to); if (lf == 0) return el_wrap_str(el_strdup(s)); size_t count = 0; const char* p = s; while ((p = strstr(p, from)) != NULL) { count++; p += lf; } size_t out_sz = ls + count * lt + 1; char* out = el_strbuf(out_sz); char* dst = out; p = s; const char* found; while ((found = strstr(p, from)) != NULL) { size_t chunk = (size_t)(found - p); memcpy(dst, p, chunk); dst += chunk; memcpy(dst, to, lt); dst += lt; p = found + lf; } strcpy(dst, p); return el_wrap_str(out); } el_val_t str_to_upper(el_val_t sv) { const char* s = EL_CSTR(sv); if (!s) return el_wrap_str(el_strdup("")); size_t n = strlen(s); char* out = el_strbuf(n); for (size_t i = 0; i < n; i++) out[i] = (char)toupper((unsigned char)s[i]); out[n] = '\0'; return el_wrap_str(out); } el_val_t str_to_lower(el_val_t sv) { const char* s = EL_CSTR(sv); if (!s) return el_wrap_str(el_strdup("")); size_t n = strlen(s); char* out = el_strbuf(n); for (size_t i = 0; i < n; i++) out[i] = (char)tolower((unsigned char)s[i]); out[n] = '\0'; return el_wrap_str(out); } el_val_t str_trim(el_val_t sv) { const char* s = EL_CSTR(sv); if (!s) return el_wrap_str(el_strdup("")); while (*s && isspace((unsigned char)*s)) s++; size_t n = strlen(s); while (n > 0 && isspace((unsigned char)s[n - 1])) n--; char* out = el_strbuf(n); memcpy(out, s, n); out[n] = '\0'; return el_wrap_str(out); } /* ── Math ────────────────────────────────────────────────────────────────── */ el_val_t el_abs(el_val_t n) { return n < 0 ? -n : n; } el_val_t el_max(el_val_t a, el_val_t b) { return a > b ? a : b; } el_val_t el_min(el_val_t a, el_val_t b) { return a < b ? a : b; } /* ── List ────────────────────────────────────────────────────────────────── */ /* * Dynamic array header: * int64_t capacity * int64_t length * el_val_t elems[] */ typedef struct { int64_t capacity; int64_t length; el_val_t elems[1]; } ElList; static ElList* list_alloc(int64_t cap) { ElList* lst = malloc(sizeof(ElList) + (size_t)(cap > 1 ? cap - 1 : 0) * sizeof(el_val_t)); if (!lst) { fputs("el_runtime: out of memory\n", stderr); exit(1); } lst->capacity = cap; lst->length = 0; return lst; } el_val_t el_list_empty(void) { return EL_STR(list_alloc(4)); } el_val_t el_list_new(el_val_t count, ...) { ElList* lst = list_alloc(count > 0 ? count : 4); va_list ap; va_start(ap, count); for (int64_t i = 0; i < count; i++) { lst->elems[i] = va_arg(ap, el_val_t); } va_end(ap); lst->length = count; return EL_STR(lst); } el_val_t el_list_len(el_val_t listv) { ElList* lst = (ElList*)(uintptr_t)listv; if (!lst) return 0; return lst->length; } el_val_t el_list_get(el_val_t listv, el_val_t index) { ElList* lst = (ElList*)(uintptr_t)listv; if (!lst) return 0; if (index < 0 || index >= lst->length) return 0; return lst->elems[index]; } el_val_t el_list_append(el_val_t listv, el_val_t elem) { ElList* lst = (ElList*)(uintptr_t)listv; if (!lst) { lst = list_alloc(4); } if (lst->length >= lst->capacity) { int64_t new_cap = lst->capacity * 2; lst = realloc(lst, sizeof(ElList) + (size_t)(new_cap - 1) * sizeof(el_val_t)); if (!lst) { fputs("el_runtime: out of memory\n", stderr); exit(1); } lst->capacity = new_cap; } lst->elems[lst->length++] = elem; return EL_STR(lst); } /* ── Map ─────────────────────────────────────────────────────────────────── */ typedef struct { int64_t count; el_val_t* keys; el_val_t* values; } ElMap; el_val_t el_map_new(el_val_t pair_count, ...) { ElMap* m = malloc(sizeof(ElMap)); if (!m) { fputs("el_runtime: out of memory\n", stderr); exit(1); } m->count = pair_count; m->keys = malloc(sizeof(el_val_t) * (size_t)(pair_count > 0 ? pair_count : 1)); m->values = malloc(sizeof(el_val_t) * (size_t)(pair_count > 0 ? pair_count : 1)); va_list ap; va_start(ap, pair_count); for (int64_t i = 0; i < pair_count; i++) { m->keys[i] = va_arg(ap, el_val_t); m->values[i] = va_arg(ap, el_val_t); } va_end(ap); return EL_STR(m); } static ElMap* as_map(el_val_t v) { return (ElMap*)(uintptr_t)v; } el_val_t el_map_get(el_val_t mapv, el_val_t keyv) { ElMap* m = as_map(mapv); const char* key = EL_CSTR(keyv); if (!m || !key) return 0; for (int64_t i = 0; i < m->count; i++) { const char* k = EL_CSTR(m->keys[i]); if (k && strcmp(k, key) == 0) return m->values[i]; } return 0; } el_val_t el_get_field(el_val_t mapv, el_val_t keyv) { return el_map_get(mapv, keyv); } el_val_t el_map_set(el_val_t mapv, el_val_t keyv, el_val_t value) { ElMap* m = as_map(mapv); const char* key = EL_CSTR(keyv); if (!m) return 0; for (int64_t i = 0; i < m->count; i++) { const char* k = EL_CSTR(m->keys[i]); if (k && strcmp(k, key) == 0) { m->values[i] = value; return mapv; } } int64_t nc = m->count + 1; m->keys = realloc(m->keys, sizeof(el_val_t) * (size_t)nc); m->values = realloc(m->values, sizeof(el_val_t) * (size_t)nc); m->keys[m->count] = keyv; m->values[m->count] = value; m->count = nc; return mapv; } /* ── HTTP stubs ──────────────────────────────────────────────────────────── */ el_val_t http_get(el_val_t url) { (void)url; return el_wrap_str(el_strdup("")); } el_val_t http_post(el_val_t url, el_val_t body) { (void)url; (void)body; return el_wrap_str(el_strdup("")); } void http_serve(el_val_t port, el_val_t handler) { (void)port; (void)handler; } /* ── Filesystem ──────────────────────────────────────────────────────────── */ el_val_t fs_read(el_val_t pathv) { const char* path = EL_CSTR(pathv); if (!path) return el_wrap_str(el_strdup("")); FILE* f = fopen(path, "rb"); if (!f) return el_wrap_str(el_strdup("")); fseek(f, 0, SEEK_END); long sz = ftell(f); rewind(f); char* buf = el_strbuf((size_t)sz); size_t got = fread(buf, 1, (size_t)sz, f); buf[got] = '\0'; fclose(f); return el_wrap_str(buf); } el_val_t fs_write(el_val_t pathv, el_val_t contentv) { const char* path = EL_CSTR(pathv); const char* content = EL_CSTR(contentv); if (!path || !content) return 0; FILE* f = fopen(path, "wb"); if (!f) return 0; size_t n = strlen(content); size_t written = fwrite(content, 1, n, f); fclose(f); return written == n ? 1 : 0; } /* ── JSON ────────────────────────────────────────────────────────────────── */ el_val_t json_get(el_val_t jsonv, el_val_t keyv) { const char* json = EL_CSTR(jsonv); const char* key = EL_CSTR(keyv); if (!json || !key) return el_wrap_str(el_strdup("")); size_t klen = strlen(key); char* pattern = el_strbuf(klen + 4); snprintf(pattern, klen + 5, "\"%s\":", key); const char* p = strstr(json, pattern); free(pattern); if (!p) return el_wrap_str(el_strdup("")); p += strlen(key) + 3; /* skip "key": */ while (*p == ' ' || *p == '\t' || *p == '\n') p++; if (*p == '"') { p++; const char* start = p; while (*p && !(*p == '"' && *(p-1) != '\\')) p++; size_t len = (size_t)(p - start); char* out = el_strbuf(len); memcpy(out, start, len); out[len] = '\0'; return el_wrap_str(out); } const char* start = p; while (*p && *p != ',' && *p != '}' && *p != ']' && *p != '\n') p++; size_t len = (size_t)(p - start); char* out = el_strbuf(len); memcpy(out, start, len); out[len] = '\0'; return el_wrap_str(out); } /* ── Process ─────────────────────────────────────────────────────────────── */ void exit_program(el_val_t code) { exit((int)code); }