From f97354e96b2d10de8220a8727ad577e47bafeae2 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Sun, 3 May 2026 02:57:53 -0500 Subject: [PATCH] add exec() and exec_bg() builtins to El runtime - exec(cmd) -> String: runs shell command, captures stdout, 30s timeout - exec_bg(cmd) -> String: forks command in background, returns PID string - add both to codegen arity table (builtin_arity) - rebuild elc with updated arity table (self-hosting, identity-verified) - update release snapshot at releases/v1.0.0-20260501/ --- dist/elc-new.c | 2234 +++++++++++++-- dist/platform/elc | Bin 414696 -> 415016 bytes dist/platform/elc-darwin-arm64 | Bin 0 -> 415016 bytes el-compiler/runtime/el_runtime.c | 56 + el-compiler/runtime/el_runtime.h | 2 + el-compiler/src/codegen.el | 5 + releases/v1.0.0-20260501/el_runtime.c | 3687 ++++++++++++++++++++++++- releases/v1.0.0-20260501/el_runtime.h | 251 +- 8 files changed, 6013 insertions(+), 222 deletions(-) create mode 100755 dist/platform/elc-darwin-arm64 diff --git a/dist/elc-new.c b/dist/elc-new.c index 4f147e0..d7ab52c 100644 --- a/dist/elc-new.c +++ b/dist/elc-new.c @@ -2,14 +2,18 @@ #include #include "el_runtime.h" -el_val_t is_digit(el_val_t ch); -el_val_t is_alpha(el_val_t ch); +el_val_t lex_is_digit(el_val_t ch); +el_val_t lex_is_alpha(el_val_t ch); el_val_t is_alnum_or_underscore(el_val_t ch); -el_val_t is_whitespace(el_val_t ch); +el_val_t lex_is_whitespace(el_val_t ch); el_val_t make_tok(el_val_t kind, el_val_t value); el_val_t keyword_kind(el_val_t word); el_val_t scan_digits(el_val_t chars, el_val_t start, el_val_t total); el_val_t scan_ident(el_val_t chars, el_val_t start, el_val_t total); +el_val_t substr_at(el_val_t chars, el_val_t start, el_val_t total, el_val_t needle); +el_val_t str_has(el_val_t s, el_val_t needle); +el_val_t looks_like_code(el_val_t s); +el_val_t strip_code_comments(el_val_t s); el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total); el_val_t lex(el_val_t source); el_val_t tok_at(el_val_t tokens, el_val_t pos); @@ -25,6 +29,7 @@ el_val_t parse_match(el_val_t tokens, el_val_t pos); el_val_t parse_pattern(el_val_t tokens, el_val_t pos); el_val_t parse_for_expr(el_val_t tokens, el_val_t pos); el_val_t parse_block(el_val_t tokens, el_val_t pos); +el_val_t is_duration_unit(el_val_t name); el_val_t parse_postfix(el_val_t tokens, el_val_t pos); el_val_t op_precedence(el_val_t kind); el_val_t is_binop(el_val_t kind); @@ -32,12 +37,16 @@ el_val_t parse_binop(el_val_t tokens, el_val_t pos, el_val_t min_prec); el_val_t parse_expr(el_val_t tokens, el_val_t pos); el_val_t parse_stmt(el_val_t tokens, el_val_t pos); el_val_t parse(el_val_t tokens); +el_val_t nibble_to_hex(el_val_t n); +el_val_t byte_to_hex2(el_val_t b); +el_val_t is_hex_digit_byte(el_val_t b); el_val_t c_escape(el_val_t s); el_val_t c_str_lit(el_val_t s); el_val_t el_type_to_c(el_val_t type_str); el_val_t emit_line(el_val_t line); el_val_t emit_blank(void); el_val_t binop_to_c(el_val_t op); +el_val_t duration_unit_nanos(el_val_t unit); el_val_t cg_expr(el_val_t expr); el_val_t next_match_id(void); el_val_t cg_match(el_val_t expr); @@ -55,7 +64,35 @@ el_val_t param_decl(el_val_t param, el_val_t idx); el_val_t params_to_c(el_val_t params); el_val_t transform_implicit_return(el_val_t body); el_val_t is_int_name(el_val_t name); +el_val_t is_instant_name(el_val_t name); +el_val_t is_duration_name(el_val_t name); el_val_t is_int_call(el_val_t call_expr); +el_val_t is_instant_call(el_val_t call_expr); +el_val_t is_duration_call(el_val_t call_expr); +el_val_t is_calendar_name(el_val_t name); +el_val_t is_caltime_name(el_val_t name); +el_val_t is_rhythm_name(el_val_t name); +el_val_t is_localdate_name(el_val_t name); +el_val_t is_localtime_name(el_val_t name); +el_val_t is_localdt_name(el_val_t name); +el_val_t is_zone_name(el_val_t name); +el_val_t is_calendar_call(el_val_t call_expr); +el_val_t is_caltime_call(el_val_t call_expr); +el_val_t is_rhythm_call(el_val_t call_expr); +el_val_t is_localdate_call(el_val_t call_expr); +el_val_t is_localtime_call(el_val_t call_expr); +el_val_t is_localdt_call(el_val_t call_expr); +el_val_t is_zone_call(el_val_t call_expr); +el_val_t is_calendar_expr(el_val_t expr); +el_val_t is_caltime_expr(el_val_t expr); +el_val_t is_rhythm_expr(el_val_t expr); +el_val_t is_localdate_expr(el_val_t expr); +el_val_t is_localtime_expr(el_val_t expr); +el_val_t is_localdt_expr(el_val_t expr); +el_val_t is_zone_expr(el_val_t expr); +el_val_t is_instant_expr(el_val_t expr); +el_val_t is_duration_expr(el_val_t expr); +el_val_t time_record_violation(el_val_t kind, el_val_t detail); el_val_t is_int_expr(el_val_t expr); el_val_t cap_record_violation(el_val_t kind, el_val_t fn_name); el_val_t is_self_formation_call(el_val_t fn_name); @@ -63,11 +100,21 @@ el_val_t is_dharma_call(el_val_t fn_name); el_val_t is_llm_call(el_val_t fn_name); el_val_t cap_check_call(el_val_t fn_name); el_val_t emit_cap_violations(void); +el_val_t emit_time_violations(void); el_val_t builtin_arity(el_val_t name); el_val_t arity_record_violation(el_val_t fn_name, el_val_t expected, el_val_t actual); el_val_t arity_check_call(el_val_t fn_name, el_val_t actual); el_val_t emit_arity_violations(void); el_val_t add_int_name(el_val_t name); +el_val_t add_instant_name(el_val_t name); +el_val_t add_duration_name(el_val_t name); +el_val_t add_calendar_name(el_val_t name); +el_val_t add_caltime_name(el_val_t name); +el_val_t add_rhythm_name(el_val_t name); +el_val_t add_localdate_name(el_val_t name); +el_val_t add_localtime_name(el_val_t name); +el_val_t add_localdt_name(el_val_t name); +el_val_t add_zone_name(el_val_t name); el_val_t build_int_names_for_params(el_val_t params); el_val_t cg_fn(el_val_t stmt); el_val_t is_fndef(el_val_t stmt); @@ -107,8 +154,14 @@ el_val_t compile_js(el_val_t source); el_val_t compile_dispatch(el_val_t tgt, el_val_t source); el_val_t detect_target(el_val_t argv); el_val_t strip_flags(el_val_t argv); +el_val_t detect_emit_header(el_val_t argv); +el_val_t type_node_to_el(el_val_t t); +el_val_t emit_header(el_val_t stmts, el_val_t hdr_path); +el_val_t dirname_of(el_val_t path); +el_val_t parse_import_line(el_val_t trimmed, el_val_t dir); +el_val_t resolve_imports(el_val_t src_path); -el_val_t is_digit(el_val_t ch) { +el_val_t lex_is_digit(el_val_t ch) { if (str_eq(ch, EL_STR("0"))) { return 1; } @@ -143,7 +196,7 @@ el_val_t is_digit(el_val_t ch) { return 0; } -el_val_t is_alpha(el_val_t ch) { +el_val_t lex_is_alpha(el_val_t ch) { if (str_eq(ch, EL_STR("a"))) { return 1; } @@ -305,10 +358,10 @@ el_val_t is_alpha(el_val_t ch) { } el_val_t is_alnum_or_underscore(el_val_t ch) { - if (is_digit(ch)) { + if (lex_is_digit(ch)) { return 1; } - if (is_alpha(ch)) { + if (lex_is_alpha(ch)) { return 1; } if (str_eq(ch, EL_STR("_"))) { @@ -318,7 +371,7 @@ el_val_t is_alnum_or_underscore(el_val_t ch) { return 0; } -el_val_t is_whitespace(el_val_t ch) { +el_val_t lex_is_whitespace(el_val_t ch) { if (str_eq(ch, EL_STR(" "))) { return 1; } @@ -467,34 +520,37 @@ el_val_t keyword_kind(el_val_t word) { if (str_eq(word, EL_STR("vessel"))) { return EL_STR("Vessel"); } + if (str_eq(word, EL_STR("extern"))) { + return EL_STR("Extern"); + } return EL_STR(""); return 0; } el_val_t scan_digits(el_val_t chars, el_val_t start, el_val_t total) { el_val_t i = start; - el_val_t text = EL_STR(""); + el_val_t parts = native_list_empty(); el_val_t running = 1; while (running) { if (i >= total) { running = 0; } else { el_val_t ch = native_list_get(chars, i); - if (is_digit(ch)) { - text = el_str_concat(text, ch); + if (lex_is_digit(ch)) { + parts = native_list_append(parts, ch); i = (i + 1); } else { running = 0; } } } - return el_map_new(2, "text", text, "pos", i); + return el_map_new(2, "text", str_join(parts, EL_STR("")), "pos", i); return 0; } el_val_t scan_ident(el_val_t chars, el_val_t start, el_val_t total) { el_val_t i = start; - el_val_t text = EL_STR(""); + el_val_t parts = native_list_empty(); el_val_t running = 1; while (running) { if (i >= total) { @@ -502,20 +558,227 @@ el_val_t scan_ident(el_val_t chars, el_val_t start, el_val_t total) { } else { el_val_t ch = native_list_get(chars, i); if (is_alnum_or_underscore(ch)) { - text = el_str_concat(text, ch); + parts = native_list_append(parts, ch); i = (i + 1); } else { running = 0; } } } - return el_map_new(2, "text", text, "pos", i); + return el_map_new(2, "text", str_join(parts, EL_STR("")), "pos", i); + return 0; +} + +el_val_t substr_at(el_val_t chars, el_val_t start, el_val_t total, el_val_t needle) { + el_val_t nchars = native_string_chars(needle); + el_val_t nlen = native_list_len(nchars); + if ((start + nlen) > total) { + return 0; + } + el_val_t i = 0; + el_val_t matched = 1; + while (i < nlen) { + el_val_t a = native_list_get(chars, (start + i)); + el_val_t b = native_list_get(nchars, i); + if (str_eq(a, b)) { + i = (i + 1); + } else { + matched = 0; + EL_NULL; + i = nlen; + } + } + return matched; + return 0; +} + +el_val_t str_has(el_val_t s, el_val_t needle) { + el_val_t chars = native_string_chars(s); + el_val_t total = native_list_len(chars); + el_val_t i = 0; + el_val_t found = 0; + while (i < total) { + if (substr_at(chars, i, total, needle)) { + found = 1; + i = total; + } else { + i = (i + 1); + } + } + return found; + return 0; +} + +el_val_t looks_like_code(el_val_t s) { + if (str_has(s, EL_STR("= total) { + scanning = 0; + } else { + el_val_t lc = native_list_get(chars, i); + if (str_eq(lc, EL_STR("\n"))) { + scanning = 0; + } else { + i = (i + 1); + } + } + } + prev = EL_STR(""); + } + } else { + if (str_eq(next_ch, EL_STR("*"))) { + i = (i + 2); + el_val_t scanning2 = 1; + while (scanning2) { + if (i >= total) { + scanning2 = 0; + } else { + el_val_t bc = native_list_get(chars, i); + if (str_eq(bc, EL_STR("*"))) { + el_val_t after = (i + 1); + if (after < total) { + el_val_t nc2 = native_list_get(chars, after); + if (str_eq(nc2, EL_STR("/"))) { + i = (after + 1); + scanning2 = 0; + } else { + i = (i + 1); + } + } else { + i = (i + 1); + } + } else { + i = (i + 1); + } + } + } + prev = EL_STR(""); + } else { + out_parts = native_list_append(out_parts, ch); + prev = ch; + i = (i + 1); + } + } + } else { + if (str_eq(ch, EL_STR("'"))) { + in_squote = 1; + out_parts = native_list_append(out_parts, ch); + prev = ch; + i = (i + 1); + } else { + if (str_eq(ch, EL_STR("\""))) { + in_dquote = 1; + out_parts = native_list_append(out_parts, ch); + prev = ch; + i = (i + 1); + } else { + if (str_eq(ch, EL_STR("`"))) { + in_btick = 1; + out_parts = native_list_append(out_parts, ch); + prev = ch; + i = (i + 1); + } else { + out_parts = native_list_append(out_parts, ch); + prev = ch; + i = (i + 1); + } + } + } + } + } + } + return str_join(out_parts, EL_STR("")); return 0; } el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total) { el_val_t i = start; - el_val_t text = EL_STR(""); + el_val_t parts = native_list_empty(); el_val_t running = 1; while (running) { if (i >= total) { @@ -527,26 +790,26 @@ el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total) { if (next_i < total) { el_val_t next_ch = native_list_get(chars, next_i); if (str_eq(next_ch, EL_STR("\""))) { - text = el_str_concat(text, EL_STR("\"")); + parts = native_list_append(parts, EL_STR("\"")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("n"))) { - text = el_str_concat(text, EL_STR("\n")); + parts = native_list_append(parts, EL_STR("\n")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("t"))) { - text = el_str_concat(text, EL_STR("\t")); + parts = native_list_append(parts, EL_STR("\t")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("r"))) { - text = el_str_concat(text, EL_STR("\r")); + parts = native_list_append(parts, EL_STR("\r")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("\\"))) { - text = el_str_concat(text, EL_STR("\\")); + parts = native_list_append(parts, EL_STR("\\")); i = (next_i + 1); } else { - text = el_str_concat(text, next_ch); + parts = native_list_append(parts, next_ch); i = (next_i + 1); } } @@ -561,13 +824,13 @@ el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total) { i = (i + 1); running = 0; } else { - text = el_str_concat(text, ch); + parts = native_list_append(parts, ch); i = (i + 1); } } } } - return el_map_new(2, "text", text, "pos", i); + return el_map_new(2, "text", str_join(parts, EL_STR("")), "pos", i); return 0; } @@ -578,7 +841,7 @@ el_val_t lex(el_val_t source) { el_val_t i = 0; while (i < total) { el_val_t ch = native_list_get(chars, i); - if (is_whitespace(ch)) { + if (lex_is_whitespace(ch)) { i = (i + 1); } else { if (str_eq(ch, EL_STR("/"))) { @@ -613,10 +876,14 @@ el_val_t lex(el_val_t source) { el_val_t result = scan_string(chars, (i + 1), total); el_val_t str_text = el_get_field(result, EL_STR("text")); el_val_t new_pos = el_get_field(result, EL_STR("pos")); - tokens = native_list_append(tokens, make_tok(EL_STR("Str"), str_text)); + el_val_t clean_text = str_text; + if (looks_like_code(str_text)) { + clean_text = strip_code_comments(str_text); + } + tokens = native_list_append(tokens, make_tok(EL_STR("Str"), clean_text)); i = new_pos; } else { - if (is_digit(ch)) { + if (lex_is_digit(ch)) { el_val_t result = scan_digits(chars, i, total); el_val_t num_text = el_get_field(result, EL_STR("text")); el_val_t new_pos = el_get_field(result, EL_STR("pos")); @@ -626,7 +893,7 @@ el_val_t lex(el_val_t source) { el_val_t after_dot = (new_pos + 1); if (after_dot < total) { el_val_t after_dot_ch = native_list_get(chars, after_dot); - if (is_digit(after_dot_ch)) { + if (lex_is_digit(after_dot_ch)) { el_val_t frac_result = scan_digits(chars, after_dot, total); el_val_t frac_text = el_get_field(frac_result, EL_STR("text")); el_val_t frac_pos = el_get_field(frac_result, EL_STR("pos")); @@ -649,7 +916,7 @@ el_val_t lex(el_val_t source) { i = new_pos; } } else { - if (is_alpha(ch) || str_eq(ch, EL_STR("_"))) { + if (lex_is_alpha(ch) || str_eq(ch, EL_STR("_"))) { el_val_t result = scan_ident(chars, i, total); el_val_t word = el_get_field(result, EL_STR("text")); el_val_t new_pos = el_get_field(result, EL_STR("pos")); @@ -1269,10 +1536,72 @@ el_val_t parse_block(el_val_t tokens, el_val_t pos) { return 0; } +el_val_t is_duration_unit(el_val_t name) { + if (str_eq(name, EL_STR("nanos"))) { + return 1; + } + if (str_eq(name, EL_STR("nano"))) { + return 1; + } + if (str_eq(name, EL_STR("millis"))) { + return 1; + } + if (str_eq(name, EL_STR("milli"))) { + return 1; + } + if (str_eq(name, EL_STR("millisecond"))) { + return 1; + } + if (str_eq(name, EL_STR("milliseconds"))) { + return 1; + } + if (str_eq(name, EL_STR("second"))) { + return 1; + } + if (str_eq(name, EL_STR("seconds"))) { + return 1; + } + if (str_eq(name, EL_STR("minute"))) { + return 1; + } + if (str_eq(name, EL_STR("minutes"))) { + return 1; + } + if (str_eq(name, EL_STR("hour"))) { + return 1; + } + if (str_eq(name, EL_STR("hours"))) { + return 1; + } + if (str_eq(name, EL_STR("day"))) { + return 1; + } + if (str_eq(name, EL_STR("days"))) { + return 1; + } + return 0; + return 0; +} + el_val_t parse_postfix(el_val_t tokens, el_val_t pos) { el_val_t r = parse_primary(tokens, pos); el_val_t node = el_get_field(r, EL_STR("node")); el_val_t p = el_get_field(r, EL_STR("pos")); + el_val_t primary_kind = el_get_field(node, EL_STR("expr")); + if (str_eq(primary_kind, EL_STR("Int"))) { + el_val_t dot_kind = tok_kind(tokens, p); + if (str_eq(dot_kind, EL_STR("Dot"))) { + el_val_t unit_kind = tok_kind(tokens, (p + 1)); + if (str_eq(unit_kind, EL_STR("Ident"))) { + el_val_t unit_name = tok_value(tokens, (p + 1)); + if (is_duration_unit(unit_name)) { + el_val_t count_str = el_get_field(node, EL_STR("value")); + node = el_map_new(3, "expr", EL_STR("DurationLit"), "count", count_str, "unit", unit_name); + p = (p + 2); + } + } + } + } el_val_t running = 1; while (running) { el_val_t k = tok_kind(tokens, p); @@ -1477,6 +1806,29 @@ el_val_t parse_stmt(el_val_t tokens, el_val_t pos) { p = el_get_field(r, EL_STR("pos")); return make_result(el_map_new(2, "stmt", EL_STR("Return"), "value", val), p); } + if (str_eq(k, EL_STR("Extern"))) { + el_val_t p = (pos + 1); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Fn"))) { + p = (p + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + el_val_t r = parse_params(tokens, p); + el_val_t params = el_get_field(r, EL_STR("params")); + p = el_get_field(r, EL_STR("pos")); + el_val_t ret_type = EL_STR(""); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Arrow"))) { + p = (p + 1); + el_val_t kt = tok_kind(tokens, p); + if (str_eq(kt, EL_STR("Ident"))) { + ret_type = tok_value(tokens, p); + } + p = skip_type(tokens, p); + } + return make_result(el_map_new(4, "stmt", EL_STR("ExternFn"), "name", name, "params", params, "ret_type", ret_type), p); + } + } if (str_eq(k, EL_STR("Fn"))) { el_val_t p = (pos + 1); el_val_t name = tok_value(tokens, p); @@ -1728,6 +2080,17 @@ el_val_t parse_stmt(el_val_t tokens, el_val_t pos) { p = expect(tokens, p, EL_STR("RBrace")); return make_result(el_map_new(4, "stmt", EL_STR("ServiceBlock"), "name", name, "sponsor", sponsor, "domain", domain), p); } + if (str_eq(k, EL_STR("Ident"))) { + el_val_t k2 = tok_kind(tokens, (pos + 1)); + if (str_eq(k2, EL_STR("Eq"))) { + el_val_t name = tok_value(tokens, pos); + el_val_t p = (pos + 2); + el_val_t r = parse_expr(tokens, p); + el_val_t val = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + return make_result(el_map_new(3, "stmt", EL_STR("Assign"), "name", name, "value", val), p); + } + } el_val_t r = parse_expr(tokens, pos); el_val_t val = el_get_field(r, EL_STR("node")); el_val_t p = el_get_field(r, EL_STR("pos")); @@ -1764,29 +2127,72 @@ el_val_t parse(el_val_t tokens) { return 0; } +el_val_t nibble_to_hex(el_val_t n) { + return str_char_at(EL_STR("0123456789abcdef"), n); + return 0; +} + +el_val_t byte_to_hex2(el_val_t b) { + el_val_t hi = (b / 16); + el_val_t lo = (b - (hi * 16)); + return el_str_concat(nibble_to_hex(hi), nibble_to_hex(lo)); + return 0; +} + +el_val_t is_hex_digit_byte(el_val_t b) { + if (b >= 48) { + if (b <= 57) { + return 1; + } + } + if (b >= 65) { + if (b <= 70) { + return 1; + } + } + if (b >= 97) { + if (b <= 102) { + return 1; + } + } + return 0; + return 0; +} + el_val_t c_escape(el_val_t s) { - el_val_t chars = native_string_chars(s); - el_val_t total = native_list_len(chars); - el_val_t out = EL_STR(""); + el_val_t total = str_len(s); + el_val_t parts = native_list_empty(); el_val_t i = 0; + el_val_t prev_was_hex_escape = 0; while (i < total) { - el_val_t ch = native_list_get(chars, i); - if (str_eq(ch, EL_STR("\""))) { - out = el_str_concat(out, EL_STR("\\\"")); + el_val_t bval = str_char_code(s, i); + if (prev_was_hex_escape) { + if (is_hex_digit_byte(bval)) { + parts = native_list_append(parts, EL_STR("\"\"")); + } + } + prev_was_hex_escape = 0; + if (bval == 34) { + parts = native_list_append(parts, EL_STR("\\\"")); } else { - if (str_eq(ch, EL_STR("\\"))) { - out = el_str_concat(out, EL_STR("\\\\")); + if (bval == 92) { + parts = native_list_append(parts, EL_STR("\\\\")); } else { - if (str_eq(ch, EL_STR("\n"))) { - out = el_str_concat(out, EL_STR("\\n")); + if (bval == 10) { + parts = native_list_append(parts, EL_STR("\\n")); } else { - if (str_eq(ch, EL_STR("\r"))) { - out = el_str_concat(out, EL_STR("\\r")); + if (bval == 13) { + parts = native_list_append(parts, EL_STR("\\r")); } else { - if (str_eq(ch, EL_STR("\t"))) { - out = el_str_concat(out, EL_STR("\\t")); + if (bval == 9) { + parts = native_list_append(parts, EL_STR("\\t")); } else { - out = el_str_concat(out, ch); + if (bval >= 128) { + parts = native_list_append(parts, el_str_concat(EL_STR("\\x"), byte_to_hex2(bval))); + prev_was_hex_escape = 1; + } else { + parts = native_list_append(parts, str_char_at(s, i)); + } } } } @@ -1794,7 +2200,7 @@ el_val_t c_escape(el_val_t s) { } i = (i + 1); } - return out; + return str_join(parts, EL_STR("")); return 0; } @@ -1877,12 +2283,65 @@ el_val_t binop_to_c(el_val_t op) { return 0; } +el_val_t duration_unit_nanos(el_val_t unit) { + if (str_eq(unit, EL_STR("nano"))) { + return EL_STR("1LL"); + } + if (str_eq(unit, EL_STR("nanos"))) { + return EL_STR("1LL"); + } + if (str_eq(unit, EL_STR("milli"))) { + return EL_STR("1000000LL"); + } + if (str_eq(unit, EL_STR("millis"))) { + return EL_STR("1000000LL"); + } + if (str_eq(unit, EL_STR("millisecond"))) { + return EL_STR("1000000LL"); + } + if (str_eq(unit, EL_STR("milliseconds"))) { + return EL_STR("1000000LL"); + } + if (str_eq(unit, EL_STR("second"))) { + return EL_STR("1000000000LL"); + } + if (str_eq(unit, EL_STR("seconds"))) { + return EL_STR("1000000000LL"); + } + if (str_eq(unit, EL_STR("minute"))) { + return EL_STR("60000000000LL"); + } + if (str_eq(unit, EL_STR("minutes"))) { + return EL_STR("60000000000LL"); + } + if (str_eq(unit, EL_STR("hour"))) { + return EL_STR("3600000000000LL"); + } + if (str_eq(unit, EL_STR("hours"))) { + return EL_STR("3600000000000LL"); + } + if (str_eq(unit, EL_STR("day"))) { + return EL_STR("86400000000000LL"); + } + if (str_eq(unit, EL_STR("days"))) { + return EL_STR("86400000000000LL"); + } + return EL_STR("1LL"); + return 0; +} + el_val_t cg_expr(el_val_t expr) { el_val_t kind = el_get_field(expr, EL_STR("expr")); if (str_eq(kind, EL_STR("Int"))) { el_val_t v = el_get_field(expr, EL_STR("value")); return v; } + if (str_eq(kind, EL_STR("DurationLit"))) { + el_val_t count = el_get_field(expr, EL_STR("count")); + el_val_t unit = el_get_field(expr, EL_STR("unit")); + el_val_t mult = duration_unit_nanos(unit); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_from_nanos((el_val_t)("), count), EL_STR("LL * ")), mult), EL_STR("))")); + } if (str_eq(kind, EL_STR("Float"))) { el_val_t v = el_get_field(expr, EL_STR("value")); return el_str_concat(el_str_concat(EL_STR("el_from_float("), v), EL_STR(")")); @@ -1923,6 +2382,234 @@ el_val_t cg_expr(el_val_t expr) { el_val_t right_c = cg_expr(right); el_val_t left_kind = el_get_field(left, EL_STR("expr")); el_val_t right_kind = el_get_field(right, EL_STR("expr")); + if (str_eq(op, EL_STR("Plus"))) { + if (str_eq(left_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (str_eq(right_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (str_eq(op, EL_STR("EqEq"))) { + if (str_eq(left_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (str_eq(right_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (str_eq(op, EL_STR("NotEq"))) { + if (str_eq(left_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (str_eq(right_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + el_val_t left_is_inst = is_instant_expr(left); + el_val_t right_is_inst = is_instant_expr(right); + el_val_t left_is_dur = is_duration_expr(left); + el_val_t right_is_dur = is_duration_expr(right); + el_val_t left_is_ld = is_localdate_expr(left); + el_val_t right_is_ld = is_localdate_expr(right); + el_val_t left_is_lt = is_localtime_expr(left); + el_val_t right_is_lt = is_localtime_expr(right); + el_val_t left_is_ct = is_caltime_expr(left); + el_val_t right_is_ct = is_caltime_expr(right); + if (left_is_ld) { + if (str_eq(op, EL_STR("Plus"))) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_local_date_add_dur("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (str_eq(op, EL_STR("Lt"))) { + if (right_is_ld) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_local_date_lt("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (str_eq(op, EL_STR("EqEq"))) { + if (right_is_ld) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_local_date_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + if (left_is_lt) { + if (str_eq(op, EL_STR("Plus"))) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_local_time_add_dur("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + if (left_is_ct) { + if (str_eq(op, EL_STR("Plus"))) { + if (right_is_ct) { + time_record_violation(EL_STR("caltime_plus_caltime"), EL_STR("CalendarTime + CalendarTime is not allowed (use cal_to_instant + Duration)")); + return EL_STR("0 /* TIME_TYPE_ERROR: CalendarTime + CalendarTime */"); + } + } + } + el_val_t any_temporal = 0; + if (left_is_inst) { + any_temporal = 1; + } + if (right_is_inst) { + any_temporal = 1; + } + if (left_is_dur) { + any_temporal = 1; + } + if (right_is_dur) { + any_temporal = 1; + } + if (any_temporal) { + if (str_eq(op, EL_STR("Plus"))) { + if (left_is_inst) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_add_dur("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (right_is_inst) { + time_record_violation(EL_STR("instant_plus_instant"), EL_STR("Instant + Instant is not allowed")); + return EL_STR("0 /* TIME_TYPE_ERROR: Instant + Instant */"); + } + } + if (left_is_dur) { + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_add_dur("), right_c), EL_STR(", ")), left_c), EL_STR(")")); + } + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_add("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (is_int_expr(right)) { + time_record_violation(EL_STR("duration_plus_int"), EL_STR("Duration + Int is not allowed (use duration_seconds(n) or N.seconds)")); + return EL_STR("0 /* TIME_TYPE_ERROR: Duration + Int */"); + } + } + if (right_is_dur) { + if (is_int_expr(left)) { + time_record_violation(EL_STR("duration_plus_int"), EL_STR("Int + Duration is not allowed")); + return EL_STR("0 /* TIME_TYPE_ERROR: Int + Duration */"); + } + } + } + if (str_eq(op, EL_STR("Minus"))) { + if (left_is_inst) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_sub_dur("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_diff("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (left_is_dur) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_sub("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (is_int_expr(right)) { + time_record_violation(EL_STR("duration_minus_int"), EL_STR("Duration - Int is not allowed")); + return EL_STR("0 /* TIME_TYPE_ERROR: Duration - Int */"); + } + } + } + if (str_eq(op, EL_STR("Star"))) { + if (left_is_dur) { + if (is_int_expr(right)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_scale("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (right_is_dur) { + if (is_int_expr(left)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_scale("), right_c), EL_STR(", ")), left_c), EL_STR(")")); + } + } + } + if (str_eq(op, EL_STR("Slash"))) { + if (left_is_dur) { + if (is_int_expr(right)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_div("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + if (str_eq(op, EL_STR("Lt"))) { + if (left_is_inst) { + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_lt("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (right_is_dur) { + time_record_violation(EL_STR("instant_cmp_duration"), EL_STR("Instant < Duration is not allowed")); + return EL_STR("0 /* TIME_TYPE_ERROR: Instant < Duration */"); + } + } + if (left_is_dur) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_lt("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + if (right_is_inst) { + time_record_violation(EL_STR("duration_cmp_instant"), EL_STR("Duration < Instant is not allowed")); + return EL_STR("0 /* TIME_TYPE_ERROR: Duration < Instant */"); + } + } + } + if (str_eq(op, EL_STR("LtEq"))) { + if (left_is_inst) { + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_le("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (left_is_dur) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_le("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + if (str_eq(op, EL_STR("Gt"))) { + if (left_is_inst) { + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_gt("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (left_is_dur) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_gt("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + if (str_eq(op, EL_STR("GtEq"))) { + if (left_is_inst) { + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_ge("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (left_is_dur) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_ge("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + if (str_eq(op, EL_STR("EqEq"))) { + if (left_is_inst) { + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (left_is_dur) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + if (str_eq(op, EL_STR("NotEq"))) { + if (left_is_inst) { + if (right_is_inst) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_instant_ne("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + if (left_is_dur) { + if (right_is_dur) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_duration_ne("), left_c), EL_STR(", ")), right_c), EL_STR(")")); + } + } + } + } if (str_eq(op, EL_STR("Plus"))) { if (str_eq(left_kind, EL_STR("Str"))) { return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")")); @@ -1993,6 +2680,11 @@ el_val_t cg_expr(el_val_t expr) { } } } + if (is_int_expr(left)) { + if (is_int_expr(right)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" == ")), right_c), EL_STR(")")); + } + } if (str_eq(left_kind, EL_STR("Str"))) { return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); } @@ -2036,6 +2728,11 @@ el_val_t cg_expr(el_val_t expr) { } } } + if (is_int_expr(left)) { + if (is_int_expr(right)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" != ")), right_c), EL_STR(")")); + } + } if (str_eq(left_kind, EL_STR("Str"))) { return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("!str_eq("), left_c), EL_STR(", ")), right_c), EL_STR(")")); } @@ -2063,21 +2760,37 @@ el_val_t cg_expr(el_val_t expr) { el_val_t args = el_get_field(expr, EL_STR("args")); el_val_t arity = native_list_len(args); el_val_t func_kind = el_get_field(func, EL_STR("expr")); - el_val_t args_c = EL_STR(""); + el_val_t args_parts = native_list_empty(); el_val_t i = 0; while (i < arity) { el_val_t arg = native_list_get(args, i); el_val_t arg_c = cg_expr(arg); - if (i > 0) { - args_c = el_str_concat(args_c, EL_STR(", ")); - } - args_c = el_str_concat(args_c, arg_c); + args_parts = native_list_append(args_parts, arg_c); i = (i + 1); } + el_val_t args_c = str_join(args_parts, EL_STR(", ")); if (str_eq(func_kind, EL_STR("Ident"))) { el_val_t fn_name = el_get_field(func, EL_STR("name")); cap_check_call(fn_name); arity_check_call(fn_name, arity); + if (str_eq(fn_name, EL_STR("sleep"))) { + if (arity == 1) { + el_val_t only_arg = native_list_get(args, 0); + if (is_duration_expr(only_arg)) { + return el_str_concat(el_str_concat(EL_STR("el_sleep_duration("), args_c), EL_STR(")")); + } + } + } + if (str_eq(fn_name, EL_STR("el_from_float"))) { + if (arity == 1) { + el_val_t only_arg = native_list_get(args, 0); + el_val_t arg_kind = el_get_field(only_arg, EL_STR("expr")); + if (str_eq(arg_kind, EL_STR("Float"))) { + el_val_t v = el_get_field(only_arg, EL_STR("value")); + return el_str_concat(el_str_concat(EL_STR("el_from_float("), v), EL_STR(")")); + } + } + } return el_str_concat(el_str_concat(el_str_concat(fn_name, EL_STR("(")), args_c), EL_STR(")")); } if (str_eq(func_kind, EL_STR("Field"))) { @@ -2115,18 +2828,15 @@ el_val_t cg_expr(el_val_t expr) { if (n == 0) { return EL_STR("el_list_empty()"); } - el_val_t items = EL_STR(""); + el_val_t items_parts = native_list_empty(); el_val_t i = 0; while (i < n) { el_val_t elem = native_list_get(elems, i); el_val_t elem_c = cg_expr(elem); - if (i > 0) { - items = el_str_concat(items, EL_STR(", ")); - } - items = el_str_concat(items, elem_c); + items_parts = native_list_append(items_parts, elem_c); i = (i + 1); } - return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_list_new("), native_int_to_str(n)), EL_STR(", ")), items), EL_STR(")")); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_list_new("), native_int_to_str(n)), EL_STR(", ")), str_join(items_parts, EL_STR(", "))), EL_STR(")")); } if (str_eq(kind, EL_STR("Map"))) { el_val_t pairs = el_get_field(expr, EL_STR("pairs")); @@ -2134,20 +2844,17 @@ el_val_t cg_expr(el_val_t expr) { if (n == 0) { return EL_STR("el_map_new(0)"); } - el_val_t items = EL_STR(""); + el_val_t items_parts = native_list_empty(); el_val_t i = 0; while (i < n) { el_val_t pair = native_list_get(pairs, i); el_val_t key = el_get_field(pair, EL_STR("key")); el_val_t val = el_get_field(pair, EL_STR("value")); el_val_t val_c = cg_expr(val); - if (i > 0) { - items = el_str_concat(items, EL_STR(", ")); - } - items = el_str_concat(el_str_concat(el_str_concat(items, c_str_lit(key)), EL_STR(", ")), val_c); + items_parts = native_list_append(items_parts, el_str_concat(el_str_concat(c_str_lit(key), EL_STR(", ")), val_c)); i = (i + 1); } - return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_map_new("), native_int_to_str(n)), EL_STR(", ")), items), EL_STR(")")); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_map_new("), native_int_to_str(n)), EL_STR(", ")), str_join(items_parts, EL_STR(", "))), EL_STR(")")); } if (str_eq(kind, EL_STR("Try"))) { el_val_t inner = el_get_field(expr, EL_STR("inner")); @@ -2183,7 +2890,8 @@ el_val_t cg_match(el_val_t expr) { el_val_t subj_var = el_str_concat(EL_STR("_match_subj_"), id); el_val_t result_var = el_str_concat(EL_STR("_match_result_"), id); el_val_t done_label = el_str_concat(EL_STR("_match_done_"), id); - el_val_t out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("({ el_val_t "), subj_var), EL_STR(" = ")), subj_c), EL_STR("; el_val_t ")), result_var), EL_STR(" = 0; ")); + el_val_t parts = native_list_empty(); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("({ el_val_t "), subj_var), EL_STR(" = ")), subj_c), EL_STR("; el_val_t ")), result_var), EL_STR(" = 0; "))); el_val_t n = native_list_len(arms); el_val_t i = 0; while (i < n) { @@ -2193,19 +2901,19 @@ el_val_t cg_match(el_val_t expr) { el_val_t pkind = el_get_field(pat, EL_STR("pattern")); el_val_t body_c = cg_expr(body); if (str_eq(pkind, EL_STR("Wildcard"))) { - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{ "), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } "))); } else { if (str_eq(pkind, EL_STR("Binding"))) { el_val_t bname = el_get_field(pat, EL_STR("name")); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ el_val_t ")), bname), EL_STR(" = ")), subj_var), EL_STR("; ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{ el_val_t "), bname), EL_STR(" = ")), subj_var), EL_STR("; ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } "))); } else { if (str_eq(pkind, EL_STR("LitInt"))) { el_val_t v = el_get_field(pat, EL_STR("value")); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" == ")), v), EL_STR(") { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("if ("), subj_var), EL_STR(" == ")), v), EL_STR(") { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } "))); } else { if (str_eq(pkind, EL_STR("LitStr"))) { el_val_t v = el_get_field(pat, EL_STR("value")); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (str_eq(")), subj_var), EL_STR(", EL_STR(")), c_str_lit(v)), EL_STR("))) { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("if (str_eq("), subj_var), EL_STR(", EL_STR(")), c_str_lit(v)), EL_STR("))) { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } "))); } else { if (str_eq(pkind, EL_STR("LitBool"))) { el_val_t v = el_get_field(pat, EL_STR("value")); @@ -2213,9 +2921,9 @@ el_val_t cg_match(el_val_t expr) { if (str_eq(v, EL_STR("true"))) { bv = EL_STR("1"); } - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" == ")), bv), EL_STR(") { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("if ("), subj_var), EL_STR(" == ")), bv), EL_STR(") { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } "))); } else { - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{ "), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } "))); } } } @@ -2223,8 +2931,8 @@ el_val_t cg_match(el_val_t expr) { } i = (i + 1); } - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, done_label), EL_STR(":; ")), result_var), EL_STR("; })")); - return out; + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(done_label, EL_STR(":; ")), result_var), EL_STR("; })"))); + return str_join(parts, EL_STR("")); return 0; } @@ -2242,41 +2950,48 @@ el_val_t next_if_id(void) { el_val_t cg_if_expr_arm(el_val_t stmts, el_val_t result_var) { el_val_t n = native_list_len(stmts); - el_val_t out = EL_STR(""); + el_val_t parts = native_list_empty(); el_val_t i = 0; while (i < n) { el_val_t s = native_list_get(stmts, i); el_val_t sk = el_get_field(s, EL_STR("stmt")); el_val_t is_last = 0; - if (str_eq(i, (n - 1))) { + if (i == (n - 1)) { is_last = 1; } if (str_eq(sk, EL_STR("Let"))) { el_val_t name = el_get_field(s, EL_STR("name")); el_val_t val = el_get_field(s, EL_STR("value")); el_val_t val_c = cg_expr(val); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("el_val_t ")), name), EL_STR(" = ")), val_c), EL_STR("; ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_val_t "), name), EL_STR(" = ")), val_c), EL_STR("; "))); } else { if (str_eq(sk, EL_STR("Return"))) { el_val_t val = el_get_field(s, EL_STR("value")); el_val_t val_c = cg_expr(val); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, result_var), EL_STR(" = (")), val_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(result_var, EL_STR(" = (")), val_c), EL_STR("); "))); } else { if (str_eq(sk, EL_STR("Expr"))) { el_val_t val = el_get_field(s, EL_STR("value")); el_val_t val_c = cg_expr(val); if (is_last) { - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, result_var), EL_STR(" = (")), val_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(result_var, EL_STR(" = (")), val_c), EL_STR("); "))); } else { - out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("(void)(")), val_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(EL_STR("(void)("), val_c), EL_STR("); "))); } } else { + if (str_eq(sk, EL_STR("Assign"))) { + el_val_t aname = el_get_field(s, EL_STR("name")); + el_val_t aval = el_get_field(s, EL_STR("value")); + el_val_t aval_c = cg_expr(aval); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(aname, EL_STR(" = ")), aval_c), EL_STR("; "))); + } else { + } } } } i = (i + 1); } - return out; + return str_join(parts, EL_STR("")); return 0; } @@ -2322,6 +3037,60 @@ el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) { if (str_eq(ltype, EL_STR("Int"))) { add_int_name(name); } + if (str_eq(ltype, EL_STR("Instant"))) { + add_instant_name(name); + } + if (str_eq(ltype, EL_STR("Duration"))) { + add_duration_name(name); + } + if (str_eq(ltype, EL_STR("Calendar"))) { + add_calendar_name(name); + } + if (str_eq(ltype, EL_STR("CalendarTime"))) { + add_caltime_name(name); + } + if (str_eq(ltype, EL_STR("Rhythm"))) { + add_rhythm_name(name); + } + if (str_eq(ltype, EL_STR("LocalDate"))) { + add_localdate_name(name); + } + if (str_eq(ltype, EL_STR("LocalTime"))) { + add_localtime_name(name); + } + if (str_eq(ltype, EL_STR("LocalDateTime"))) { + add_localdt_name(name); + } + if (str_eq(ltype, EL_STR("Zone"))) { + add_zone_name(name); + } + if (is_instant_expr(val)) { + add_instant_name(name); + } + if (is_duration_expr(val)) { + add_duration_name(name); + } + if (is_calendar_expr(val)) { + add_calendar_name(name); + } + if (is_caltime_expr(val)) { + add_caltime_name(name); + } + if (is_rhythm_expr(val)) { + add_rhythm_name(name); + } + if (is_localdate_expr(val)) { + add_localdate_name(name); + } + if (is_localtime_expr(val)) { + add_localtime_name(name); + } + if (is_localdt_expr(val)) { + add_localdt_name(name); + } + if (is_zone_expr(val)) { + add_zone_name(name); + } el_val_t vk = el_get_field(val, EL_STR("expr")); if (str_eq(vk, EL_STR("Int"))) { add_int_name(name); @@ -2345,6 +3114,13 @@ el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) { } return declared; } + if (str_eq(kind, EL_STR("Assign"))) { + el_val_t name = el_get_field(stmt, EL_STR("name")); + el_val_t val = el_get_field(stmt, EL_STR("value")); + el_val_t val_c = cg_expr(val); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, name), EL_STR(" = ")), val_c), EL_STR(";"))); + return declared; + } if (str_eq(kind, EL_STR("Expr"))) { el_val_t val = el_get_field(stmt, EL_STR("value")); el_val_t val_kind = el_get_field(val, EL_STR("expr")); @@ -2389,6 +3165,9 @@ el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) { if (str_eq(kind, EL_STR("Import"))) { return declared; } + if (str_eq(kind, EL_STR("ExternFn"))) { + return declared; + } if (str_eq(kind, EL_STR("CgiBlock"))) { return declared; } @@ -2424,14 +3203,7 @@ el_val_t strip_outer_parens(el_val_t s) { i = (i + 1); } if (balanced) { - el_val_t inner = EL_STR(""); - el_val_t j = 1; - while (j < (n - 1)) { - el_val_t ch = native_list_get(chars, j); - inner = el_str_concat(inner, ch); - j = (j + 1); - } - return inner; + return str_slice(s, 1, (n - 1)); } } } @@ -2506,18 +3278,15 @@ el_val_t params_to_c(el_val_t params) { if (n == 0) { return EL_STR("void"); } - el_val_t out = EL_STR(""); + el_val_t parts = native_list_empty(); el_val_t i = 0; while (i < n) { el_val_t param = native_list_get(params, i); el_val_t decl = param_decl(param, i); - if (i > 0) { - out = el_str_concat(out, EL_STR(", ")); - } - out = el_str_concat(out, decl); + parts = native_list_append(parts, decl); i = (i + 1); } - return out; + return str_join(parts, EL_STR(", ")); return 0; } @@ -2560,6 +3329,24 @@ el_val_t is_int_name(el_val_t name) { return 0; } +el_val_t is_instant_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__instant_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_duration_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__duration_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + el_val_t is_int_call(el_val_t call_expr) { el_val_t func = el_get_field(call_expr, EL_STR("func")); el_val_t fk = el_get_field(func, EL_STR("expr")); @@ -2579,6 +3366,33 @@ el_val_t is_int_call(el_val_t call_expr) { if (str_eq(name, EL_STR("str_char_code"))) { return 1; } + if (str_eq(name, EL_STR("str_count"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_chars"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_bytes"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_lines"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_words"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_letters"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_digits"))) { + return 1; + } + if (str_eq(name, EL_STR("str_last_index_of"))) { + return 1; + } + if (str_eq(name, EL_STR("str_find_chars"))) { + return 1; + } if (str_eq(name, EL_STR("native_list_len"))) { return 1; } @@ -2627,10 +3441,527 @@ el_val_t is_int_call(el_val_t call_expr) { if (str_eq(name, EL_STR("float_to_int"))) { return 1; } + if (str_eq(name, EL_STR("unix_timestamp"))) { + return 1; + } + if (str_eq(name, EL_STR("instant_to_unix_seconds"))) { + return 1; + } + if (str_eq(name, EL_STR("instant_to_unix_millis"))) { + return 1; + } + if (str_eq(name, EL_STR("duration_to_seconds"))) { + return 1; + } + if (str_eq(name, EL_STR("duration_to_millis"))) { + return 1; + } + if (str_eq(name, EL_STR("duration_to_nanos"))) { + return 1; + } return 0; return 0; } +el_val_t is_instant_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("now"))) { + return 1; + } + if (str_eq(name, EL_STR("el_now_instant"))) { + return 1; + } + if (str_eq(name, EL_STR("unix_seconds"))) { + return 1; + } + if (str_eq(name, EL_STR("unix_millis"))) { + return 1; + } + if (str_eq(name, EL_STR("instant_from_iso8601"))) { + return 1; + } + if (str_eq(name, EL_STR("el_instant_add_dur"))) { + return 1; + } + if (str_eq(name, EL_STR("el_instant_sub_dur"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_duration_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("el_duration_from_nanos"))) { + return 1; + } + if (str_eq(name, EL_STR("duration_seconds"))) { + return 1; + } + if (str_eq(name, EL_STR("duration_millis"))) { + return 1; + } + if (str_eq(name, EL_STR("duration_nanos"))) { + return 1; + } + if (str_eq(name, EL_STR("el_instant_diff"))) { + return 1; + } + if (str_eq(name, EL_STR("el_duration_add"))) { + return 1; + } + if (str_eq(name, EL_STR("el_duration_sub"))) { + return 1; + } + if (str_eq(name, EL_STR("el_duration_scale"))) { + return 1; + } + if (str_eq(name, EL_STR("el_duration_div"))) { + return 1; + } + if (str_eq(name, EL_STR("ttl_cache_age"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_calendar_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__calendar_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_caltime_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__caltime_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_rhythm_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__rhythm_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_localdate_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__localdate_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_localtime_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__localtime_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_localdt_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__localdt_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_zone_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__zone_names")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","))); + return 0; +} + +el_val_t is_calendar_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("earth_calendar"))) { + return 1; + } + if (str_eq(name, EL_STR("earth_calendar_default"))) { + return 1; + } + if (str_eq(name, EL_STR("mars_calendar"))) { + return 1; + } + if (str_eq(name, EL_STR("cycle_calendar"))) { + return 1; + } + if (str_eq(name, EL_STR("no_cycle_calendar"))) { + return 1; + } + if (str_eq(name, EL_STR("relative_calendar"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_caltime_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("now_in"))) { + return 1; + } + if (str_eq(name, EL_STR("in_calendar"))) { + return 1; + } + if (str_eq(name, EL_STR("cal_in"))) { + return 1; + } + if (str_eq(name, EL_STR("zoned"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_rhythm_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("rhythm_cycle_start"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_cycle_phase"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_duration"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_session_start"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_event"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_and"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_or"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_weekday"))) { + return 1; + } + if (str_eq(name, EL_STR("rhythm_weekly_at"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_localdate_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("local_date"))) { + return 1; + } + if (str_eq(name, EL_STR("el_local_date_add_dur"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_localtime_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("local_time"))) { + return 1; + } + if (str_eq(name, EL_STR("el_local_time_add_dur"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_localdt_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("local_datetime"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_zone_call(el_val_t call_expr) { + el_val_t func = el_get_field(call_expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (!str_eq(fk, EL_STR("Ident"))) { + return 0; + } + el_val_t name = el_get_field(func, EL_STR("name")); + if (str_eq(name, EL_STR("zone"))) { + return 1; + } + if (str_eq(name, EL_STR("zone_utc"))) { + return 1; + } + if (str_eq(name, EL_STR("zone_local"))) { + return 1; + } + if (str_eq(name, EL_STR("zone_offset"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_calendar_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + return is_calendar_name(el_get_field(expr, EL_STR("name"))); + } + if (str_eq(k, EL_STR("Call"))) { + return is_calendar_call(expr); + } + return 0; + return 0; +} + +el_val_t is_caltime_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + return is_caltime_name(el_get_field(expr, EL_STR("name"))); + } + if (str_eq(k, EL_STR("Call"))) { + return is_caltime_call(expr); + } + return 0; + return 0; +} + +el_val_t is_rhythm_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + return is_rhythm_name(el_get_field(expr, EL_STR("name"))); + } + if (str_eq(k, EL_STR("Call"))) { + return is_rhythm_call(expr); + } + return 0; + return 0; +} + +el_val_t is_localdate_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + return is_localdate_name(el_get_field(expr, EL_STR("name"))); + } + if (str_eq(k, EL_STR("Call"))) { + return is_localdate_call(expr); + } + return 0; + return 0; +} + +el_val_t is_localtime_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + return is_localtime_name(el_get_field(expr, EL_STR("name"))); + } + if (str_eq(k, EL_STR("Call"))) { + return is_localtime_call(expr); + } + return 0; + return 0; +} + +el_val_t is_localdt_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + return is_localdt_name(el_get_field(expr, EL_STR("name"))); + } + if (str_eq(k, EL_STR("Call"))) { + return is_localdt_call(expr); + } + return 0; + return 0; +} + +el_val_t is_zone_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + return is_zone_name(el_get_field(expr, EL_STR("name"))); + } + if (str_eq(k, EL_STR("Call"))) { + return is_zone_call(expr); + } + return 0; + return 0; +} + +el_val_t is_instant_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Ident"))) { + el_val_t name = el_get_field(expr, EL_STR("name")); + return is_instant_name(name); + } + if (str_eq(k, EL_STR("Call"))) { + return is_instant_call(expr); + } + if (str_eq(k, EL_STR("BinOp"))) { + el_val_t op = el_get_field(expr, EL_STR("op")); + if (str_eq(op, EL_STR("Plus"))) { + if (is_instant_expr(el_get_field(expr, EL_STR("left")))) { + if (is_duration_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + if (is_duration_expr(el_get_field(expr, EL_STR("left")))) { + if (is_instant_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Minus"))) { + if (is_instant_expr(el_get_field(expr, EL_STR("left")))) { + if (is_duration_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + return 0; + } + return 0; + return 0; +} + +el_val_t is_duration_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("DurationLit"))) { + return 1; + } + if (str_eq(k, EL_STR("Ident"))) { + el_val_t name = el_get_field(expr, EL_STR("name")); + return is_duration_name(name); + } + if (str_eq(k, EL_STR("Call"))) { + return is_duration_call(expr); + } + if (str_eq(k, EL_STR("Neg"))) { + return is_duration_expr(el_get_field(expr, EL_STR("inner"))); + } + if (str_eq(k, EL_STR("BinOp"))) { + el_val_t op = el_get_field(expr, EL_STR("op")); + if (str_eq(op, EL_STR("Plus"))) { + if (is_duration_expr(el_get_field(expr, EL_STR("left")))) { + if (is_duration_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Minus"))) { + if (is_duration_expr(el_get_field(expr, EL_STR("left")))) { + if (is_duration_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + if (is_instant_expr(el_get_field(expr, EL_STR("left")))) { + if (is_instant_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Star"))) { + if (is_duration_expr(el_get_field(expr, EL_STR("left")))) { + if (is_int_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + if (is_int_expr(el_get_field(expr, EL_STR("left")))) { + if (is_duration_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Slash"))) { + if (is_duration_expr(el_get_field(expr, EL_STR("left")))) { + if (is_int_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + return 0; + } + return 0; + return 0; +} + +el_val_t time_record_violation(el_val_t kind, el_val_t detail) { + el_val_t csv = state_get(EL_STR("__time_violations")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t entry = el_str_concat(el_str_concat(kind, EL_STR(":")), detail); + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), entry), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__time_violations"), el_str_concat(el_str_concat(csv, entry), EL_STR(","))); + return 1; + return 0; +} + el_val_t is_int_expr(el_val_t expr) { el_val_t k = el_get_field(expr, EL_STR("expr")); if (str_eq(k, EL_STR("Int"))) { @@ -2841,7 +4172,33 @@ el_val_t emit_cap_violations(void) { if (colon > 0) { el_val_t kind = str_slice(entry, 0, colon); el_val_t fn_name = str_slice(entry, (colon + 1), str_len(entry)); - emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("#error \"capability violation: '"), kind), EL_STR("' programs may not call '")), fn_name), EL_STR("' (self-formation primitive — only 'cgi' programs may use it)\""))); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("#error \"capability violation: '"), kind), EL_STR("' programs may not call '")), fn_name), EL_STR("' (self-formation primitive - only 'cgi' programs may use it)\""))); + } + i = ((i + next_comma) + 1); + } + return 0; +} + +el_val_t emit_time_violations(void) { + el_val_t csv = state_get(EL_STR("__time_violations")); + if (str_eq(csv, EL_STR(""))) { + return 0; + } + if (str_eq(csv, EL_STR(","))) { + return 0; + } + el_val_t n = str_len(csv); + el_val_t i = 1; + while (i < n) { + el_val_t next_comma = str_index_of(str_slice(csv, i, n), EL_STR(",")); + if (next_comma < 0) { + return 0; + } + el_val_t entry = str_slice(csv, i, (i + next_comma)); + el_val_t colon = str_index_of(entry, EL_STR(":")); + if (colon > 0) { + el_val_t detail = str_slice(entry, (colon + 1), str_len(entry)); + emit_line(el_str_concat(el_str_concat(EL_STR("#error \"temporal type error: "), detail), EL_STR("\""))); } i = ((i + next_comma) + 1); } @@ -2927,6 +4284,93 @@ el_val_t builtin_arity(el_val_t name) { if (str_eq(name, EL_STR("str_upper"))) { return 1; } + if (str_eq(name, EL_STR("str_count"))) { + return 2; + } + if (str_eq(name, EL_STR("str_count_chars"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_bytes"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_lines"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_words"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_letters"))) { + return 1; + } + if (str_eq(name, EL_STR("str_count_digits"))) { + return 1; + } + if (str_eq(name, EL_STR("str_index_of_all"))) { + return 2; + } + if (str_eq(name, EL_STR("str_last_index_of"))) { + return 2; + } + if (str_eq(name, EL_STR("str_find_chars"))) { + return 2; + } + if (str_eq(name, EL_STR("str_repeat"))) { + return 2; + } + if (str_eq(name, EL_STR("str_reverse"))) { + return 1; + } + if (str_eq(name, EL_STR("str_strip_prefix"))) { + return 2; + } + if (str_eq(name, EL_STR("str_strip_suffix"))) { + return 2; + } + if (str_eq(name, EL_STR("str_strip_chars"))) { + return 2; + } + if (str_eq(name, EL_STR("str_lstrip"))) { + return 1; + } + if (str_eq(name, EL_STR("str_rstrip"))) { + return 1; + } + if (str_eq(name, EL_STR("is_letter"))) { + return 1; + } + if (str_eq(name, EL_STR("is_digit"))) { + return 1; + } + if (str_eq(name, EL_STR("is_alphanumeric"))) { + return 1; + } + if (str_eq(name, EL_STR("is_whitespace"))) { + return 1; + } + if (str_eq(name, EL_STR("is_punctuation"))) { + return 1; + } + if (str_eq(name, EL_STR("is_uppercase"))) { + return 1; + } + if (str_eq(name, EL_STR("is_lowercase"))) { + return 1; + } + if (str_eq(name, EL_STR("str_split_lines"))) { + return 1; + } + if (str_eq(name, EL_STR("str_split_chars"))) { + return 1; + } + if (str_eq(name, EL_STR("str_split_n"))) { + return 3; + } + if (str_eq(name, EL_STR("str_join"))) { + return 2; + } + if (str_eq(name, EL_STR("el_html_sanitize"))) { + return 2; + } if (str_eq(name, EL_STR("el_abs"))) { return 1; } @@ -3125,6 +4569,18 @@ el_val_t builtin_arity(el_val_t name) { if (str_eq(name, EL_STR("exit_program"))) { return 1; } + if (str_eq(name, EL_STR("exec_command"))) { + return 1; + } + if (str_eq(name, EL_STR("exec_capture"))) { + return 1; + } + if (str_eq(name, EL_STR("exec"))) { + return 1; + } + if (str_eq(name, EL_STR("exec_bg"))) { + return 1; + } if (str_eq(name, EL_STR("dharma_connect"))) { return 1; } @@ -3360,9 +4816,7 @@ el_val_t emit_arity_violations(void) { el_val_t add_int_name(el_val_t name) { el_val_t csv = state_get(EL_STR("__int_names")); if (str_eq(csv, EL_STR(""))) { - csv; - EL_NULL; - EL_STR(","); + csv = EL_STR(","); } el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); if (str_contains(csv, key)) { @@ -3373,8 +4827,143 @@ el_val_t add_int_name(el_val_t name) { return 0; } +el_val_t add_instant_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__instant_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__instant_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_duration_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__duration_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__duration_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_calendar_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__calendar_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__calendar_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_caltime_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__caltime_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__caltime_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_rhythm_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__rhythm_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__rhythm_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_localdate_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__localdate_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__localdate_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_localtime_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__localtime_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__localtime_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_localdt_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__localdt_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__localdt_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t add_zone_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__zone_names")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); + if (str_contains(csv, key)) { + return 1; + } + state_set(EL_STR("__zone_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + el_val_t build_int_names_for_params(el_val_t params) { state_set(EL_STR("__int_names"), EL_STR(",")); + state_set(EL_STR("__instant_names"), EL_STR(",")); + state_set(EL_STR("__duration_names"), EL_STR(",")); + state_set(EL_STR("__calendar_names"), EL_STR(",")); + state_set(EL_STR("__caltime_names"), EL_STR(",")); + state_set(EL_STR("__rhythm_names"), EL_STR(",")); + state_set(EL_STR("__localdate_names"), EL_STR(",")); + state_set(EL_STR("__localtime_names"), EL_STR(",")); + state_set(EL_STR("__localdt_names"), EL_STR(",")); + state_set(EL_STR("__zone_names"), EL_STR(",")); el_val_t np = native_list_len(params); el_val_t pi = 0; while (pi < np) { @@ -3384,6 +4973,33 @@ el_val_t build_int_names_for_params(el_val_t params) { if (str_eq(ptype, EL_STR("Int"))) { add_int_name(pname); } + if (str_eq(ptype, EL_STR("Instant"))) { + add_instant_name(pname); + } + if (str_eq(ptype, EL_STR("Duration"))) { + add_duration_name(pname); + } + if (str_eq(ptype, EL_STR("Calendar"))) { + add_calendar_name(pname); + } + if (str_eq(ptype, EL_STR("CalendarTime"))) { + add_caltime_name(pname); + } + if (str_eq(ptype, EL_STR("Rhythm"))) { + add_rhythm_name(pname); + } + if (str_eq(ptype, EL_STR("LocalDate"))) { + add_localdate_name(pname); + } + if (str_eq(ptype, EL_STR("LocalTime"))) { + add_localtime_name(pname); + } + if (str_eq(ptype, EL_STR("LocalDateTime"))) { + add_localdt_name(pname); + } + if (str_eq(ptype, EL_STR("Zone"))) { + add_zone_name(pname); + } pi = (pi + 1); } return 1; @@ -3450,6 +5066,9 @@ el_val_t is_top_level_decl(el_val_t stmt) { if (str_eq(kind, EL_STR("CgiBlock"))) { return 1; } + if (str_eq(kind, EL_STR("ExternFn"))) { + return 1; + } return 0; return 0; } @@ -3675,7 +5294,7 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { } if (cgi_count >= 1) { if (svc_count >= 1) { - emit_line(EL_STR("#error \"El: program declares both cgi and service blocks (mutually exclusive — pick one)\"")); + emit_line(EL_STR("#error \"El: program declares both cgi and service blocks (mutually exclusive - pick one)\"")); } } el_val_t kind = EL_STR("utility"); @@ -3688,9 +5307,37 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { state_set(EL_STR("__program_kind"), kind); state_set(EL_STR("__cap_violations"), EL_STR("")); state_set(EL_STR("__arity_violations"), EL_STR("")); + state_set(EL_STR("__time_violations"), EL_STR("")); emit_line(EL_STR("#include ")); emit_line(EL_STR("#include ")); emit_line(EL_STR("#include \"el_runtime.h\"")); + el_val_t imp_n = native_list_len(stmts); + el_val_t imp_i = 0; + while (imp_i < imp_n) { + el_val_t imp_stmt = native_list_get(stmts, imp_i); + el_val_t imp_kind = el_get_field(imp_stmt, EL_STR("stmt")); + if (str_eq(imp_kind, EL_STR("Import"))) { + el_val_t imp_path = el_get_field(imp_stmt, EL_STR("path")); + el_val_t imp_path_len = str_len(imp_path); + el_val_t imp_last_slash = (-1); + el_val_t imp_j = 0; + while (imp_j < imp_path_len) { + el_val_t imp_c = str_slice(imp_path, imp_j, (imp_j + 1)); + if (str_eq(imp_c, EL_STR("/"))) { + imp_last_slash = imp_j; + } + imp_j = (imp_j + 1); + } + el_val_t imp_base = str_slice(imp_path, (imp_last_slash + 1), imp_path_len); + el_val_t imp_base_len = str_len(imp_base); + el_val_t imp_bname = imp_base; + if (str_ends_with(imp_base, EL_STR(".el"))) { + imp_bname = str_slice(imp_base, 0, (imp_base_len - 3)); + } + emit_line(el_str_concat(el_str_concat(EL_STR("#include \""), imp_bname), EL_STR(".elh\""))); + } + imp_i = (imp_i + 1); + } emit_blank(); el_val_t n = native_list_len(stmts); el_val_t i = 0; @@ -3705,6 +5352,12 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_val_t "), fn_name), EL_STR("(")), params_c), EL_STR(");"))); } } + if (str_eq(kind, EL_STR("ExternFn"))) { + el_val_t fn_name = el_get_field(stmt, EL_STR("name")); + el_val_t params = el_get_field(stmt, EL_STR("params")); + el_val_t params_c = params_to_c(params); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_val_t "), fn_name), EL_STR("(")), params_c), EL_STR(");"))); + } i = (i + 1); } emit_blank(); @@ -3732,6 +5385,33 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { if (has_toplevel_lets) { emit_blank(); } + el_val_t has_el_main = 0; + el_val_t has_toplevel_stmts = 0; + i = 0; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + el_val_t sk = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(sk, EL_STR("FnDef"))) { + el_val_t fn_name_chk = el_get_field(stmt, EL_STR("name")); + if (str_eq(fn_name_chk, EL_STR("main"))) { + has_el_main = 1; + } + } + if (!is_fndef(stmt)) { + if (!is_top_level_decl(stmt)) { + if (!str_eq(sk, EL_STR("Let"))) { + has_toplevel_stmts = 1; + } + } + } + i = (i + 1); + } + el_val_t is_library = 0; + if (!has_el_main) { + if (!has_toplevel_stmts) { + is_library = 1; + } + } i = 0; while (i < n) { el_val_t stmt = native_list_get(stmts, i); @@ -3740,8 +5420,11 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { } i = (i + 1); } - emit_line(EL_STR("int main(int argc, char** argv) {")); - emit_line(EL_STR(" el_runtime_init_args(argc, argv);")); + if (is_library) { + return EL_STR(""); + } + emit_line(EL_STR("int main(int _argc, char** _argv) {")); + emit_line(EL_STR(" el_runtime_init_args(_argc, _argv);")); if (cgi_count >= 1) { el_val_t cname = el_get_field(cgi_block, EL_STR("name")); el_val_t cdid = el_get_field(cgi_block, EL_STR("dharma_id")); @@ -3770,6 +5453,24 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { } i = (i + 1); } + el_val_t el_main_body = native_list_empty(); + i = 0; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + if (is_fndef(stmt)) { + el_val_t fn_name = el_get_field(stmt, EL_STR("name")); + if (str_eq(fn_name, EL_STR("main"))) { + el_val_t body = el_get_field(stmt, EL_STR("body")); + el_val_t bn = native_list_len(body); + el_val_t bi = 0; + while (bi < bn) { + el_main_body = native_list_append(el_main_body, native_list_get(body, bi)); + bi = (bi + 1); + } + } + } + i = (i + 1); + } i = 0; while (i < n) { el_val_t stmt = native_list_get(stmts, i); @@ -3783,11 +5484,19 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { el_release(stmt); i = (i + 1); } + el_val_t mn = native_list_len(el_main_body); + el_val_t mi = 0; + while (mi < mn) { + el_val_t mstmt = native_list_get(el_main_body, mi); + main_decl = cg_stmt(mstmt, EL_STR(" "), main_decl); + mi = (mi + 1); + } emit_line(EL_STR(" return 0;")); emit_line(EL_STR("}")); emit_blank(); emit_cap_violations(); emit_arity_violations(); + emit_time_violations(); return EL_STR(""); return 0; } @@ -3795,26 +5504,26 @@ el_val_t codegen(el_val_t stmts, el_val_t source) { el_val_t js_escape(el_val_t s) { el_val_t chars = native_string_chars(s); el_val_t total = native_list_len(chars); - el_val_t out = EL_STR(""); + el_val_t parts = native_list_empty(); el_val_t i = 0; while (i < total) { el_val_t ch = native_list_get(chars, i); if (str_eq(ch, EL_STR("\""))) { - out = el_str_concat(out, EL_STR("\\\"")); + parts = native_list_append(parts, EL_STR("\\\"")); } else { if (str_eq(ch, EL_STR("\\"))) { - out = el_str_concat(out, EL_STR("\\\\")); + parts = native_list_append(parts, EL_STR("\\\\")); } else { if (str_eq(ch, EL_STR("\n"))) { - out = el_str_concat(out, EL_STR("\\n")); + parts = native_list_append(parts, EL_STR("\\n")); } else { if (str_eq(ch, EL_STR("\r"))) { - out = el_str_concat(out, EL_STR("\\r")); + parts = native_list_append(parts, EL_STR("\\r")); } else { if (str_eq(ch, EL_STR("\t"))) { - out = el_str_concat(out, EL_STR("\\t")); + parts = native_list_append(parts, EL_STR("\\t")); } else { - out = el_str_concat(out, ch); + parts = native_list_append(parts, ch); } } } @@ -3822,7 +5531,7 @@ el_val_t js_escape(el_val_t s) { } i = (i + 1); } - return out; + return str_join(parts, EL_STR("")); return 0; } @@ -3897,9 +5606,7 @@ el_val_t js_is_int_name(el_val_t name) { el_val_t js_add_int_name(el_val_t name) { el_val_t csv = state_get(EL_STR("__js_int_names")); if (str_eq(csv, EL_STR(""))) { - csv; - EL_NULL; - EL_STR(","); + csv = EL_STR(","); } el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")); if (str_contains(csv, key)) { @@ -3983,6 +5690,54 @@ el_val_t js_cg_expr(el_val_t expr) { el_val_t v = el_get_field(expr, EL_STR("value")); return v; } + if (str_eq(kind, EL_STR("DurationLit"))) { + el_val_t count = el_get_field(expr, EL_STR("count")); + el_val_t unit = el_get_field(expr, EL_STR("unit")); + el_val_t mult_ns = EL_STR("1"); + if (str_eq(unit, EL_STR("nano"))) { + mult_ns = EL_STR("1"); + } + if (str_eq(unit, EL_STR("nanos"))) { + mult_ns = EL_STR("1"); + } + if (str_eq(unit, EL_STR("milli"))) { + mult_ns = EL_STR("1000000"); + } + if (str_eq(unit, EL_STR("millis"))) { + mult_ns = EL_STR("1000000"); + } + if (str_eq(unit, EL_STR("millisecond"))) { + mult_ns = EL_STR("1000000"); + } + if (str_eq(unit, EL_STR("milliseconds"))) { + mult_ns = EL_STR("1000000"); + } + if (str_eq(unit, EL_STR("second"))) { + mult_ns = EL_STR("1000000000"); + } + if (str_eq(unit, EL_STR("seconds"))) { + mult_ns = EL_STR("1000000000"); + } + if (str_eq(unit, EL_STR("minute"))) { + mult_ns = EL_STR("60000000000"); + } + if (str_eq(unit, EL_STR("minutes"))) { + mult_ns = EL_STR("60000000000"); + } + if (str_eq(unit, EL_STR("hour"))) { + mult_ns = EL_STR("3600000000000"); + } + if (str_eq(unit, EL_STR("hours"))) { + mult_ns = EL_STR("3600000000000"); + } + if (str_eq(unit, EL_STR("day"))) { + mult_ns = EL_STR("86400000000000"); + } + if (str_eq(unit, EL_STR("days"))) { + mult_ns = EL_STR("86400000000000"); + } + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), count), EL_STR(" * ")), mult_ns), EL_STR(")")); + } if (str_eq(kind, EL_STR("Float"))) { el_val_t v = el_get_field(expr, EL_STR("value")); return v; @@ -4171,17 +5926,15 @@ el_val_t js_cg_expr(el_val_t expr) { el_val_t args = el_get_field(expr, EL_STR("args")); el_val_t arity = native_list_len(args); el_val_t func_kind = el_get_field(func, EL_STR("expr")); - el_val_t args_c = EL_STR(""); + el_val_t args_parts = native_list_empty(); el_val_t i = 0; while (i < arity) { el_val_t arg = native_list_get(args, i); el_val_t arg_c = js_cg_expr(arg); - if (i > 0) { - args_c = el_str_concat(args_c, EL_STR(", ")); - } - args_c = el_str_concat(args_c, arg_c); + args_parts = native_list_append(args_parts, arg_c); i = (i + 1); } + el_val_t args_c = str_join(args_parts, EL_STR(", ")); if (str_eq(func_kind, EL_STR("Ident"))) { el_val_t fn_name = el_get_field(func, EL_STR("name")); return el_str_concat(el_str_concat(el_str_concat(fn_name, EL_STR("(")), args_c), EL_STR(")")); @@ -4221,18 +5974,15 @@ el_val_t js_cg_expr(el_val_t expr) { if (n == 0) { return EL_STR("[]"); } - el_val_t items = EL_STR(""); + el_val_t items_parts = native_list_empty(); el_val_t i = 0; while (i < n) { el_val_t elem = native_list_get(elems, i); el_val_t elem_c = js_cg_expr(elem); - if (i > 0) { - items = el_str_concat(items, EL_STR(", ")); - } - items = el_str_concat(items, elem_c); + items_parts = native_list_append(items_parts, elem_c); i = (i + 1); } - return el_str_concat(el_str_concat(EL_STR("["), items), EL_STR("]")); + return el_str_concat(el_str_concat(EL_STR("["), str_join(items_parts, EL_STR(", "))), EL_STR("]")); } if (str_eq(kind, EL_STR("Map"))) { el_val_t pairs = el_get_field(expr, EL_STR("pairs")); @@ -4240,20 +5990,17 @@ el_val_t js_cg_expr(el_val_t expr) { if (n == 0) { return EL_STR("{}"); } - el_val_t items = EL_STR(""); + el_val_t items_parts = native_list_empty(); el_val_t i = 0; while (i < n) { el_val_t pair = native_list_get(pairs, i); el_val_t key = el_get_field(pair, EL_STR("key")); el_val_t val = el_get_field(pair, EL_STR("value")); el_val_t val_c = js_cg_expr(val); - if (i > 0) { - items = el_str_concat(items, EL_STR(", ")); - } - items = el_str_concat(el_str_concat(el_str_concat(items, js_str_lit(key)), EL_STR(": ")), val_c); + items_parts = native_list_append(items_parts, el_str_concat(el_str_concat(js_str_lit(key), EL_STR(": ")), val_c)); i = (i + 1); } - return el_str_concat(el_str_concat(EL_STR("{"), items), EL_STR("}")); + return el_str_concat(el_str_concat(EL_STR("{"), str_join(items_parts, EL_STR(", "))), EL_STR("}")); } if (str_eq(kind, EL_STR("Try"))) { el_val_t inner = el_get_field(expr, EL_STR("inner")); @@ -4289,7 +6036,8 @@ el_val_t js_cg_match(el_val_t expr) { el_val_t subj_c = js_cg_expr(subject); el_val_t id = js_next_match_id(); el_val_t subj_var = el_str_concat(EL_STR("_match_subj_"), id); - el_val_t out = el_str_concat(el_str_concat(EL_STR("(("), subj_var), EL_STR(") => { ")); + el_val_t parts = native_list_empty(); + parts = native_list_append(parts, el_str_concat(el_str_concat(EL_STR("(("), subj_var), EL_STR(") => { "))); el_val_t n = native_list_len(arms); el_val_t i = 0; while (i < n) { @@ -4299,19 +6047,19 @@ el_val_t js_cg_match(el_val_t expr) { el_val_t pkind = el_get_field(pat, EL_STR("pattern")); el_val_t body_c = js_cg_expr(body); if (str_eq(pkind, EL_STR("Wildcard"))) { - out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("return (")), body_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(EL_STR("return ("), body_c), EL_STR("); "))); } else { if (str_eq(pkind, EL_STR("Binding"))) { el_val_t bname = el_get_field(pat, EL_STR("name")); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ const ")), bname), EL_STR(" = ")), subj_var), EL_STR("; return (")), body_c), EL_STR("); } ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{ const "), bname), EL_STR(" = ")), subj_var), EL_STR("; return (")), body_c), EL_STR("); } "))); } else { if (str_eq(pkind, EL_STR("LitInt"))) { el_val_t v = el_get_field(pat, EL_STR("value")); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" === ")), v), EL_STR(") return (")), body_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("if ("), subj_var), EL_STR(" === ")), v), EL_STR(") return (")), body_c), EL_STR("); "))); } else { if (str_eq(pkind, EL_STR("LitStr"))) { el_val_t v = el_get_field(pat, EL_STR("value")); - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (str_eq(")), subj_var), EL_STR(", ")), js_str_lit(v)), EL_STR(")) return (")), body_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("if (str_eq("), subj_var), EL_STR(", ")), js_str_lit(v)), EL_STR(")) return (")), body_c), EL_STR("); "))); } else { if (str_eq(pkind, EL_STR("LitBool"))) { el_val_t v = el_get_field(pat, EL_STR("value")); @@ -4319,9 +6067,9 @@ el_val_t js_cg_match(el_val_t expr) { if (str_eq(v, EL_STR("true"))) { bv = EL_STR("true"); } - out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" === ")), bv), EL_STR(") return (")), body_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("if ("), subj_var), EL_STR(" === ")), bv), EL_STR(") return (")), body_c), EL_STR("); "))); } else { - out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("return (")), body_c), EL_STR("); ")); + parts = native_list_append(parts, el_str_concat(el_str_concat(EL_STR("return ("), body_c), EL_STR("); "))); } } } @@ -4329,8 +6077,8 @@ el_val_t js_cg_match(el_val_t expr) { } i = (i + 1); } - out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("return null; })(")), subj_c), EL_STR(")")); - return out; + parts = native_list_append(parts, el_str_concat(el_str_concat(EL_STR("return null; })("), subj_c), EL_STR(")"))); + return str_join(parts, EL_STR("")); return 0; } @@ -4381,6 +6129,13 @@ el_val_t js_cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) { } return declared; } + if (str_eq(kind, EL_STR("Assign"))) { + el_val_t name = el_get_field(stmt, EL_STR("name")); + el_val_t val = el_get_field(stmt, EL_STR("value")); + el_val_t val_c = js_cg_expr(val); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, name), EL_STR(" = ")), val_c), EL_STR(";"))); + return declared; + } if (str_eq(kind, EL_STR("Expr"))) { el_val_t val = el_get_field(stmt, EL_STR("value")); el_val_t val_kind = el_get_field(val, EL_STR("expr")); @@ -4427,12 +6182,12 @@ el_val_t js_cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) { } if (str_eq(kind, EL_STR("CgiBlock"))) { el_val_t cname = el_get_field(stmt, EL_STR("name")); - js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("// cgi block '")), cname), EL_STR("' — no-op in JS target (server-side concept)"))); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("// cgi block '")), cname), EL_STR("' - no-op in JS target (server-side concept)"))); return declared; } if (str_eq(kind, EL_STR("ServiceBlock"))) { el_val_t sname = el_get_field(stmt, EL_STR("name")); - js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("// service block '")), sname), EL_STR("' — no-op in JS target"))); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("// service block '")), sname), EL_STR("' - no-op in JS target"))); return declared; } return declared; @@ -4467,14 +6222,7 @@ el_val_t js_strip_outer_parens(el_val_t s) { i = (i + 1); } if (balanced) { - el_val_t inner = EL_STR(""); - el_val_t j = 1; - while (j < (n - 1)) { - el_val_t ch = native_list_get(chars, j); - inner = el_str_concat(inner, ch); - j = (j + 1); - } - return inner; + return str_slice(s, 1, (n - 1)); } } } @@ -4535,18 +6283,15 @@ el_val_t js_params_str(el_val_t params) { if (n == 0) { return EL_STR(""); } - el_val_t out = EL_STR(""); + el_val_t parts = native_list_empty(); el_val_t i = 0; while (i < n) { el_val_t param = native_list_get(params, i); el_val_t name = el_get_field(param, EL_STR("name")); - if (i > 0) { - out = el_str_concat(out, EL_STR(", ")); - } - out = el_str_concat(out, name); + parts = native_list_append(parts, name); i = (i + 1); } - return out; + return str_join(parts, EL_STR(", ")); return 0; } @@ -4766,8 +6511,189 @@ el_val_t strip_flags(el_val_t argv) { return 0; } -int main(int argc, char** argv) { - el_runtime_init_args(argc, argv); +el_val_t detect_emit_header(el_val_t argv) { + el_val_t n = native_list_len(argv); + el_val_t i = 0; + while (i < n) { + el_val_t a = native_list_get(argv, i); + if (str_eq(a, EL_STR("--emit-header"))) { + return 1; + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t type_node_to_el(el_val_t t) { + el_val_t k = el_get_field(t, EL_STR("kind")); + if (str_eq(k, EL_STR("Simple"))) { + return el_get_field(t, EL_STR("name")); + } + if (str_eq(k, EL_STR("List"))) { + el_val_t inner = type_node_to_el(el_get_field(t, EL_STR("inner"))); + return el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR("]")); + } + if (str_eq(k, EL_STR("Map"))) { + el_val_t kt = type_node_to_el(el_get_field(t, EL_STR("key"))); + el_val_t vt = type_node_to_el(el_get_field(t, EL_STR("val"))); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("Map<"), kt), EL_STR(", ")), vt), EL_STR(">")); + } + return EL_STR("Any"); + return 0; +} + +el_val_t emit_header(el_val_t stmts, el_val_t hdr_path) { + el_val_t n = native_list_len(stmts); + el_val_t i = 0; + el_val_t parts = native_list_empty(); + parts = native_list_append(parts, EL_STR("// auto-generated by elc --emit-header - do not edit\n")); + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + el_val_t kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("FnDef"))) { + el_val_t name = el_get_field(stmt, EL_STR("name")); + if (!str_eq(name, EL_STR("main"))) { + el_val_t params = el_get_field(stmt, EL_STR("params")); + el_val_t ret_type = el_get_field(stmt, EL_STR("ret_type")); + el_val_t np = native_list_len(params); + el_val_t pi = 0; + el_val_t param_parts = native_list_empty(); + while (pi < np) { + el_val_t param = native_list_get(params, pi); + el_val_t pname = el_get_field(param, EL_STR("name")); + el_val_t ptype = el_get_field(param, EL_STR("type")); + if (str_eq(ptype, EL_STR(""))) { + ptype = EL_STR("Any"); + } + param_parts = native_list_append(param_parts, el_str_concat(el_str_concat(pname, EL_STR(": ")), ptype)); + pi = (pi + 1); + } + el_val_t params_str = str_join(param_parts, EL_STR(", ")); + el_val_t ret_str = ret_type; + if (str_eq(ret_str, EL_STR(""))) { + ret_str = EL_STR("Any"); + } + el_val_t sig = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("extern fn "), name), EL_STR("(")), params_str), EL_STR(") -> ")), ret_str); + parts = native_list_append(parts, el_str_concat(sig, EL_STR("\n"))); + } + } + i = (i + 1); + } + el_val_t content = str_join(parts, EL_STR("")); + el_val_t ok = fs_write(hdr_path, content); + return 0; +} + +el_val_t dirname_of(el_val_t path) { + el_val_t n = str_len(path); + el_val_t i = (n - 1); + while (i >= 0) { + el_val_t c = str_slice(path, i, (i + 1)); + if (str_eq(c, EL_STR("/"))) { + return str_slice(path, 0, i); + } + i = (i - 1); + } + return EL_STR("."); + return 0; +} + +el_val_t parse_import_line(el_val_t trimmed, el_val_t dir) { + if (str_starts_with(trimmed, EL_STR("import \""))) { + el_val_t after = str_slice(trimmed, 8, str_len(trimmed)); + el_val_t q = str_index_of(after, EL_STR("\"")); + if (q > 0) { + el_val_t mod = str_slice(after, 0, q); + return el_str_concat(el_str_concat(dir, EL_STR("/")), mod); + } + } + if (str_starts_with(trimmed, EL_STR("from "))) { + el_val_t after = str_slice(trimmed, 5, str_len(trimmed)); + el_val_t sp = str_index_of(after, EL_STR(" ")); + if (sp > 0) { + el_val_t mod_raw = str_slice(after, 0, sp); + el_val_t mod = str_trim(mod_raw); + if (!str_eq(mod, EL_STR(""))) { + return el_str_concat(el_str_concat(el_str_concat(dir, EL_STR("/")), mod), EL_STR(".el")); + } + } + } + return EL_STR(""); + return 0; +} + +el_val_t resolve_imports(el_val_t src_path) { + el_val_t seen_key = el_str_concat(EL_STR("__elc_imp__:"), src_path); + el_val_t already = state_get(seen_key); + if (!str_eq(already, EL_STR(""))) { + return EL_STR(""); + } + state_set(seen_key, EL_STR("1")); + el_val_t source = fs_read(src_path); + el_val_t dir = dirname_of(src_path); + el_val_t lines = str_split(source, EL_STR("\n")); + el_val_t n = native_list_len(lines); + el_val_t prefix_chunks = native_list_empty(); + el_val_t body_chunks = native_list_empty(); + el_val_t i = 0; + while (i < n) { + el_val_t line = native_list_get(lines, i); + el_val_t trimmed = str_trim(line); + el_val_t imp_path = parse_import_line(trimmed, dir); + if (!str_eq(imp_path, EL_STR(""))) { + el_val_t imp_elh_path = el_str_concat(str_slice(imp_path, 0, (str_len(imp_path) - 3)), EL_STR(".elh")); + el_val_t imp_elh = fs_read(imp_elh_path); + if (!str_eq(imp_elh, EL_STR(""))) { + el_val_t seen_imp_key = el_str_concat(EL_STR("__elc_imp__:"), imp_path); + state_set(seen_imp_key, EL_STR("1")); + prefix_chunks = native_list_append(prefix_chunks, imp_elh); + } else { + el_val_t imp_body = resolve_imports(imp_path); + prefix_chunks = native_list_append(prefix_chunks, imp_body); + } + } else { + body_chunks = native_list_append(body_chunks, el_str_concat(line, EL_STR("\n"))); + } + i = (i + 1); + } + return el_str_concat(str_join(prefix_chunks, EL_STR("")), str_join(body_chunks, EL_STR(""))); + return 0; +} + +int main(int _argc, char** _argv) { + el_runtime_init_args(_argc, _argv); + el_val_t argv = args(); + el_val_t tgt = detect_target(argv); + el_val_t do_emit_header = detect_emit_header(argv); + el_val_t positional = strip_flags(argv); + el_val_t argc = native_list_len(positional); + if (argc < 1) { + println(EL_STR("el-compiler: usage: elc [--target=c|js] [--emit-header] []")); + exit(1); + } + el_val_t src_path = native_list_get(positional, 0); + if (do_emit_header) { + el_val_t raw_source = fs_read(src_path); + el_val_t hdr_tokens = lex(raw_source); + el_val_t hdr_stmts = parse(hdr_tokens); + el_release(hdr_tokens); + el_val_t hdr_path = el_str_concat(str_slice(src_path, 0, (str_len(src_path) - 3)), EL_STR(".elh")); + emit_header(hdr_stmts, hdr_path); + el_release(hdr_stmts); + } + el_val_t source = resolve_imports(src_path); + el_val_t out = compile_dispatch(tgt, source); + if (argc >= 2) { + el_val_t out_path = native_list_get(positional, 1); + el_val_t ok = fs_write(out_path, out); + if (ok) { + exit(0); + } else { + println(EL_STR("el-compiler: failed to write output")); + exit(1); + } + } return 0; } diff --git a/dist/platform/elc b/dist/platform/elc index 5acf296e1f3e9de7494b8fc47ec31ddca1120590..9ec7b437d7698b2492c6f3120b169cc61b29821e 100755 GIT binary patch delta 113156 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 delta 111602 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 W^UIk*ANz$3nIJ8ok+NW$3IG5xZ#5nO diff --git a/dist/platform/elc-darwin-arm64 b/dist/platform/elc-darwin-arm64 new file mode 100755 index 0000000000000000000000000000000000000000..9ec7b437d7698b2492c6f3120b169cc61b29821e GIT binary patch literal 415016 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ฉ@RkrA!bwH3J$Ku25= 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 #include /* dlsym for http_set_handler fallback */ #include +#include #include #include #include @@ -1876,6 +1877,61 @@ el_val_t exec_capture(el_val_t cmdv) { return el_wrap_str(b.buf); } +// exec — run a shell command via /bin/sh, capture stdout, return as String. +// Times out after 30 seconds. Returns "" on any error. +// El name: exec(cmd) -> String +el_val_t exec(el_val_t cmdv) { + const char* cmd = EL_CSTR(cmdv); + if (!cmd || !*cmd) return el_wrap_str(el_strdup("")); + /* Build a time-limited command: wrap with timeout(1) if available, + * otherwise rely on the 30s read loop guard below. We use the simple + * popen approach with a deadline measured by wall clock so the caller + * is never blocked indefinitely. */ + FILE* f = popen(cmd, "r"); + if (!f) return el_wrap_str(el_strdup("")); + JsonBuf b; jb_init(&b); + char buf[4096]; + /* 30-second wall-clock deadline */ + time_t deadline = time(NULL) + 30; + while (time(NULL) < deadline) { + if (fgets(buf, sizeof(buf), f) == NULL) break; + jb_puts(&b, buf); + } + pclose(f); + return el_wrap_str(b.buf); +} + +// exec_bg — run a shell command in background, return PID as String. +// The child process runs independently; the caller is not blocked. +// Returns "" on fork failure. +// El name: exec_bg(cmd) -> String +el_val_t exec_bg(el_val_t cmdv) { + const char* cmd = EL_CSTR(cmdv); + if (!cmd || !*cmd) return el_wrap_str(el_strdup("")); + pid_t pid = fork(); + if (pid < 0) { + /* fork failed */ + return el_wrap_str(el_strdup("")); + } + if (pid == 0) { + /* child: detach from parent's stdio, exec via shell */ + setsid(); + int devnull = open("/dev/null", O_RDWR); + if (devnull >= 0) { + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); + } + execl("/bin/sh", "sh", "-c", cmd, (char*)NULL); + _exit(127); + } + /* parent: convert pid to string and return immediately */ + char pidbuf[32]; + snprintf(pidbuf, sizeof(pidbuf), "%d", (int)pid); + return el_wrap_str(el_strdup(pidbuf)); +} + el_val_t fs_list(el_val_t pathv) { const char* path = EL_CSTR(pathv); el_val_t lst = el_list_empty(); diff --git a/el-compiler/runtime/el_runtime.h b/el-compiler/runtime/el_runtime.h index 90da132..72bbf4b 100644 --- a/el-compiler/runtime/el_runtime.h +++ b/el-compiler/runtime/el_runtime.h @@ -742,6 +742,8 @@ el_val_t map_set(el_val_t map, el_val_t key, el_val_t value); /* el_map_set */ /* ── Subprocess execution ────────────────────────────────────────────────── */ el_val_t exec_command(el_val_t cmd); /* run shell command, return exit code */ el_val_t exec_capture(el_val_t cmd); /* run shell command, capture stdout */ +el_val_t exec(el_val_t cmd); /* exec(cmd) → stdout String (30s timeout) */ +el_val_t exec_bg(el_val_t cmd); /* exec_bg(cmd) → PID String (non-blocking) */ el_val_t emit_log(el_val_t level, el_val_t msg, el_val_t fields_json); el_val_t emit_metric(el_val_t name, el_val_t value, el_val_t tags_json); diff --git a/el-compiler/src/codegen.el b/el-compiler/src/codegen.el index 4868fd7..81b164d 100644 --- a/el-compiler/src/codegen.el +++ b/el-compiler/src/codegen.el @@ -2049,6 +2049,11 @@ fn builtin_arity(name: String) -> Int { if str_eq(name, "bool_to_str") { return 1 } // Process if str_eq(name, "exit_program") { return 1 } + // Subprocess execution + if str_eq(name, "exec_command") { return 1 } + if str_eq(name, "exec_capture") { return 1 } + if str_eq(name, "exec") { return 1 } + if str_eq(name, "exec_bg") { return 1 } // CGI / DHARMA if str_eq(name, "dharma_connect") { return 1 } if str_eq(name, "dharma_send") { return 2 } diff --git a/releases/v1.0.0-20260501/el_runtime.c b/releases/v1.0.0-20260501/el_runtime.c index ab09b89..0ba1432 100644 --- a/releases/v1.0.0-20260501/el_runtime.c +++ b/releases/v1.0.0-20260501/el_runtime.c @@ -38,6 +38,7 @@ #include #include /* dlsym for http_set_handler fallback */ #include +#include #include #include #include @@ -1264,9 +1265,78 @@ static int http_parse_envelope(const char* s, int* out_status, return 1; } +/* Lightweight `__status__` envelope: if the body's first key is `__status__` + * and its value is a numeric literal, lift the status to the HTTP layer and + * strip the marker from the body before sending. This is the common case for + * El handlers that want to return 4xx/5xx without going through + * http_response() — they just prepend `{"__status__":,...}` to the JSON + * they were already returning. + * + * We deliberately recognise ONLY the first-key form so the contract is cheap + * to detect and unambiguous: `{"__status__":401,"error":"unauthorized"}` is + * an envelope, but `{"error":"...","__status__":401}` is not. Product code + * controls placement. + * + * On success returns 1 with *out_status set and *out_body_alloc populated + * with a freshly malloc'd body (caller frees). On failure returns 0 and + * leaves outputs untouched. */ +static int http_parse_status_envelope(const char* s, int* out_status, + char** out_body_alloc) { + if (!s) return 0; + const char* p = s; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + if (*p != '{') return 0; + p++; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + static const char marker[] = "\"__status__\""; + size_t mlen = sizeof(marker) - 1; + if (strncmp(p, marker, mlen) != 0) return 0; + p += mlen; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + if (*p != ':') return 0; + p++; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + if (*p < '0' || *p > '9') return 0; /* non-numeric -> not an envelope */ + int status = 0; + while (*p >= '0' && *p <= '9') { + status = status * 10 + (*p - '0'); + p++; + } + if (status < 100 || status > 599) return 0; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + /* Two trailing shapes accepted: + * ,"k":v,...} -> body becomes {"k":v,...} + * } -> body becomes {} + * Anything else (e.g. `:` re-appearing, garbage) drops the envelope so + * we don't strip what we shouldn't. */ + if (*p == '}') { + *out_status = status; + *out_body_alloc = el_strdup("{}"); + return 1; + } + if (*p != ',') return 0; + p++; /* skip the comma; the rest of the object follows */ + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + /* Build the trimmed body: '{' + remainder. */ + size_t rest_len = strlen(p); + char* out = (char*)malloc(rest_len + 2); + if (!out) return 0; + out[0] = '{'; + memcpy(out + 1, p, rest_len); + out[rest_len + 1] = '\0'; + *out_status = status; + *out_body_alloc = out; + return 1; +} + /* Send a fully-built HTTP response. If `body` starts with the envelope tag, * unpack status/headers/body. Otherwise emit the historical 200-OK with * auto-detected Content-Type. */ +/* Thread-local flag: if 1, http_send_response writes status + headers but + * NO body (HEAD method behaviour). Set by http_worker before calling + * http_send_response, cleared after. */ +static __thread int _tl_http_head_only = 0; + static void http_send_response(int fd, const char* body) { if (!body) body = ""; @@ -1278,12 +1348,26 @@ static void http_send_response(int fd, const char* body) { &env_headers_map, &env_body, &env_parsed_root); + /* If the rich http_response() envelope didn't claim this body, try the + * lightweight `__status__` form. This second envelope is malloc-backed so + * we route it through env_body and let the existing cleanup path free it + * — same lifetime contract, no special case at the bottom of the + * function. */ + if (!is_envelope) { + char* trimmed = NULL; + if (http_parse_status_envelope(body, &status, &trimmed)) { + env_body = trimmed; + is_envelope = 1; + } + } + const char* eff_body = is_envelope ? env_body : body; /* Use the real byte count from fs_read if available (handles binary files * with embedded null bytes — PNG, WOFF2, etc.). Fall back to strlen for * normal text/JSON responses where _tl_fs_read_len is 0. */ size_t blen = (_tl_fs_read_len > 0) ? _tl_fs_read_len : strlen(eff_body); _tl_fs_read_len = 0; /* consume — one-shot per response */ + int head_only = _tl_http_head_only; JsonBuf hdrs; jb_init(&hdrs); int saw_content_type = 0; @@ -1319,7 +1403,10 @@ static void http_send_response(int fd, const char* body) { if (http_send_all(fd, status_line, (size_t)sl) == 0 && http_send_all(fd, hdrs.buf, hdrs.len) == 0 && http_send_all(fd, tail, (size_t)tl) == 0 - && http_send_all(fd, eff_body, blen) == 0) { + && (head_only + /* HEAD requests echo headers + Content-Length but no body. */ + ? 1 + : http_send_all(fd, eff_body, blen) == 0)) { /* sent successfully */ } @@ -1340,9 +1427,14 @@ static void* http_worker(void* arg) { if (http_read_request(fd, &method, &path, &body, NULL) == 0) { http_handler_fn h = http_lookup_active(); char* response = NULL; + /* HEAD: dispatch as GET so existing handlers respond with the same + * body, but flag the response writer to emit headers only. RFC 9110 + * requires HEAD to mirror GET headers + Content-Length without body. */ + int head_only = (method && strcmp(method, "HEAD") == 0); + const char* dispatch_method = head_only ? "GET" : method; el_request_start(); /* begin per-request arena */ if (h) { - el_val_t r = h(EL_STR(method), EL_STR(path), EL_STR(body)); + el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), EL_STR(body)); const char* rs = EL_CSTR(r); /* Copy response out BEFORE arena teardown. * For binary files, _tl_fs_read_len holds the real byte count — @@ -1355,7 +1447,9 @@ static void* http_worker(void* arg) { response = el_strdup_persist("el-runtime: no http handler registered"); } el_request_end(); /* free all intermediate strings */ + _tl_http_head_only = head_only; http_send_response(fd, response); + _tl_http_head_only = 0; free(response); } free(method); free(path); free(body); @@ -1376,22 +1470,25 @@ void http_serve(el_val_t port, el_val_t handler) { } int p = (int)port; if (p <= 0 || p > 65535) { fprintf(stderr, "http_serve: invalid port %d\n", p); return; } - int sock = socket(AF_INET, SOCK_STREAM, 0); + /* Dual-stack: AF_INET6 with IPV6_V6ONLY=0 accepts both IPv4 and IPv6. + * This makes `localhost` work in browsers that resolve it to ::1 first. */ + int sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); return; } - int yes = 1; + int yes = 1; int no = 0; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - struct sockaddr_in addr; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)); + struct sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons((uint16_t)p); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = htons((uint16_t)p); if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); close(sock); return; } if (listen(sock, 64) < 0) { perror("listen"); close(sock); return; } - fprintf(stderr, "[http] listening on 0.0.0.0:%d\n", p); + fprintf(stderr, "[http] listening on [::]:%d (dual-stack)\n", p); while (1) { - struct sockaddr_in cli; + struct sockaddr_in6 cli; socklen_t clen = sizeof(cli); int cfd = accept(sock, (struct sockaddr*)&cli, &clen); if (cfd < 0) { @@ -1580,10 +1677,12 @@ static void* http_worker_v2(void* arg) { if (http_read_request(fd, &method, &path, &body, &hdr_block) == 0) { http_handler4_fn h = http_lookup_active_v2(); char* response = NULL; + int head_only = (method && strcmp(method, "HEAD") == 0); + const char* dispatch_method = head_only ? "GET" : method; el_request_start(); /* begin per-request arena */ if (h) { el_val_t hmap = http_build_headers_map(hdr_block ? hdr_block : ""); - el_val_t r = h(EL_STR(method), EL_STR(path), hmap, EL_STR(body)); + el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), hmap, EL_STR(body)); const char* rs = EL_CSTR(r); size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0); response = malloc(rlen + 1); @@ -1596,7 +1695,9 @@ static void* http_worker_v2(void* arg) { "(call http_set_handler_v2)"); } el_request_end(); /* free all intermediate strings */ + _tl_http_head_only = head_only; http_send_response(fd, response); + _tl_http_head_only = 0; free(response); } free(method); free(path); free(body); free(hdr_block); @@ -1618,22 +1719,24 @@ void http_serve_v2(el_val_t port, el_val_t handler) { fprintf(stderr, "http_serve_v2: invalid port %d\n", p); return; } - int sock = socket(AF_INET, SOCK_STREAM, 0); + /* Dual-stack: same as http_serve - AF_INET6 + IPV6_V6ONLY=0. */ + int sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); return; } - int yes = 1; + int yes = 1; int no = 0; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - struct sockaddr_in addr; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)); + struct sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons((uint16_t)p); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = htons((uint16_t)p); if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); close(sock); return; } if (listen(sock, 64) < 0) { perror("listen"); close(sock); return; } - fprintf(stderr, "[http v2] listening on 0.0.0.0:%d\n", p); + fprintf(stderr, "[http v2] listening on [::]:%d (dual-stack)\n", p); while (1) { - struct sockaddr_in cli; + struct sockaddr_in6 cli; socklen_t clen = sizeof(cli); int cfd = accept(sock, (struct sockaddr*)&cli, &clen); if (cfd < 0) { @@ -1751,6 +1854,84 @@ el_val_t fs_write_bytes(el_val_t pathv, el_val_t bytesv, el_val_t lengthv) { return 1; } +// exec_command — run a shell command, return exit code (0 = success). +// Used by elb and other El tooling to invoke subprocesses. +el_val_t exec_command(el_val_t cmdv) { + const char* cmd = EL_CSTR(cmdv); + if (!cmd) return (el_val_t)(int64_t)-1; + int ret = system(cmd); + return (el_val_t)(int64_t)ret; +} + +// exec_capture — run a shell command, capture stdout, return as String. +// Returns "" on failure. +el_val_t exec_capture(el_val_t cmdv) { + const char* cmd = EL_CSTR(cmdv); + if (!cmd) return el_wrap_str(el_strdup("")); + FILE* f = popen(cmd, "r"); + if (!f) return el_wrap_str(el_strdup("")); + JsonBuf b; jb_init(&b); + char buf[4096]; + while (fgets(buf, sizeof(buf), f)) jb_puts(&b, buf); + pclose(f); + return el_wrap_str(b.buf); +} + +// exec — run a shell command via /bin/sh, capture stdout, return as String. +// Times out after 30 seconds. Returns "" on any error. +// El name: exec(cmd) -> String +el_val_t exec(el_val_t cmdv) { + const char* cmd = EL_CSTR(cmdv); + if (!cmd || !*cmd) return el_wrap_str(el_strdup("")); + /* Build a time-limited command: wrap with timeout(1) if available, + * otherwise rely on the 30s read loop guard below. We use the simple + * popen approach with a deadline measured by wall clock so the caller + * is never blocked indefinitely. */ + FILE* f = popen(cmd, "r"); + if (!f) return el_wrap_str(el_strdup("")); + JsonBuf b; jb_init(&b); + char buf[4096]; + /* 30-second wall-clock deadline */ + time_t deadline = time(NULL) + 30; + while (time(NULL) < deadline) { + if (fgets(buf, sizeof(buf), f) == NULL) break; + jb_puts(&b, buf); + } + pclose(f); + return el_wrap_str(b.buf); +} + +// exec_bg — run a shell command in background, return PID as String. +// The child process runs independently; the caller is not blocked. +// Returns "" on fork failure. +// El name: exec_bg(cmd) -> String +el_val_t exec_bg(el_val_t cmdv) { + const char* cmd = EL_CSTR(cmdv); + if (!cmd || !*cmd) return el_wrap_str(el_strdup("")); + pid_t pid = fork(); + if (pid < 0) { + /* fork failed */ + return el_wrap_str(el_strdup("")); + } + if (pid == 0) { + /* child: detach from parent's stdio, exec via shell */ + setsid(); + int devnull = open("/dev/null", O_RDWR); + if (devnull >= 0) { + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); + } + execl("/bin/sh", "sh", "-c", cmd, (char*)NULL); + _exit(127); + } + /* parent: convert pid to string and return immediately */ + char pidbuf[32]; + snprintf(pidbuf, sizeof(pidbuf), "%d", (int)pid); + return el_wrap_str(el_strdup(pidbuf)); +} + el_val_t fs_list(el_val_t pathv) { const char* path = EL_CSTR(pathv); el_val_t lst = el_list_empty(); @@ -1871,32 +2052,603 @@ el_val_t url_decode(el_val_t sv) { return el_wrap_str(out); } +/* ── HTML allowlist sanitizer ──────────────────────────────────────────────── + * el_html_sanitize(input, allowlist_json) + * + * Strict allowlist HTML cleaner. Replaces the older denylist patterns + * (str_replace cascades that wrapped dangerous tags in HTML comments and + * renamed `on*` attributes). The denylist approach is fragile: comment- + * wrapping can be re-broken by a literal `-->` inside an attacker-supplied + * attribute value, and every new attack vector requires a code change. + * + * Design: + * - Single-pass byte-level state machine. + * - Tag and attribute names are matched case-insensitively against the + * allowlist. Unknown tags are dropped entirely (the open and close + * markers are stripped; their inner text content survives, escaped). + * - A small set of "dangerous container" tags (script, style, iframe, + * object, embed, form, plus a few rarer ones) drop themselves AND + * their full subtree — text between `` is + * CDATA-like and must not be re-emitted as escaped text either. + * - Comments (), doctype (), CDATA (), + * and processing instructions () are dropped entirely. + * - Text content outside dropped subtrees is HTML-escaped (&, <, >, ", '). + * - Attribute values are unquoted/dequoted, then re-emitted with double + * quotes around the cleanly-escaped value. + * - For `` and any `src` attribute, the URL scheme is validated: + * only http:, https:, mailto:, fragment-only `#anchor`, or relative + * paths are allowed. Anything else (javascript:, data:, vbscript:, + * about:, file:, etc.) drops the attribute. + * - Self-closing void tags (br, hr, img, etc.) emit without a close tag. + * - Malformed input (unclosed tag at EOF, bad attribute syntax) drops + * the pending tag and continues. Pre-encoded entities (<, &, + * etc.) are passed through verbatim — the browser will decode them + * safely on render. + * + * Allowlist format (JSON string): + * {"p":[],"a":["href","title"],"strong":[],...} + * - Key = lowercase tag name. + * - Value = JSON array of allowed attribute names (lowercase). + * - Empty array means tag allowed but no attributes survive. + * + * Output is a freshly-allocated arena-tracked el_val_t string. */ + +/* Internal byte buffer with realloc-doubling. Used during sanitization; + * the final result is copied into an arena-tracked el_strbuf so the caller + * sees standard runtime memory semantics. */ +typedef struct { + char* data; + size_t len; + size_t cap; +} html_buf_t; + +static void html_buf_init(html_buf_t* b) { + b->cap = 256; + b->data = malloc(b->cap); + if (!b->data) { fputs("el_runtime: out of memory\n", stderr); exit(1); } + b->len = 0; +} + +static void html_buf_grow(html_buf_t* b, size_t need) { + if (b->len + need + 1 <= b->cap) return; + size_t nc = b->cap; + while (b->len + need + 1 > nc) nc *= 2; + char* nd = realloc(b->data, nc); + if (!nd) { fputs("el_runtime: out of memory\n", stderr); exit(1); } + b->data = nd; + b->cap = nc; +} + +static void html_buf_putc(html_buf_t* b, char c) { + html_buf_grow(b, 1); + b->data[b->len++] = c; +} + +static void html_buf_puts(html_buf_t* b, const char* s) { + if (!s) return; + size_t n = strlen(s); + html_buf_grow(b, n); + memcpy(b->data + b->len, s, n); + b->len += n; +} + +static void html_buf_free(html_buf_t* b) { + free(b->data); + b->data = NULL; + b->len = b->cap = 0; +} + +/* ASCII tolower, locale-independent. */ +static int html_tolower(int c) { + return (c >= 'A' && c <= 'Z') ? c + 32 : c; +} + +/* Case-insensitive ASCII compare of [a, a+n) against c-string `s`. + * Returns 1 iff lengths match and bytes are equal under tolower. */ +static int html_ieq_n(const char* a, size_t n, const char* s) { + if (!a || !s) return 0; + if (strlen(s) != n) return 0; + for (size_t i = 0; i < n; i++) { + if (html_tolower((unsigned char)a[i]) != html_tolower((unsigned char)s[i])) return 0; + } + return 1; +} + +/* Case-insensitive ASCII compare of two byte slices. */ +static int html_iemem(const char* a, const char* b, size_t n) { + for (size_t i = 0; i < n; i++) { + if (html_tolower((unsigned char)a[i]) != html_tolower((unsigned char)b[i])) return 0; + } + return 1; +} + +/* Walk a JSON allowlist object and find the value (an array) for a given + * tag key, comparing case-insensitively. On hit returns a pointer to the + * opening `[` of the array and writes the byte length of the array span + * (including the brackets) to *out_len. On miss returns NULL. + * + * The parser is intentionally tiny: it does not handle escapes inside + * keys (allowlist authors do not need them), and it relies on balanced + * brackets/quotes within the value array. */ +static const char* html_allowlist_find(const char* allow, const char* tag, + size_t tag_len, size_t* out_len) { + if (!allow) return NULL; + const char* p = allow; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + if (*p != '{') return NULL; + p++; + while (*p) { + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',') p++; + if (*p == '}' || *p == 0) return NULL; + if (*p != '"') return NULL; + p++; + const char* k = p; + while (*p && *p != '"') p++; + if (*p != '"') return NULL; + size_t klen = (size_t)(p - k); + p++; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + if (*p != ':') return NULL; + p++; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + if (*p != '[') return NULL; + const char* arr_start = p; + int depth = 0; + int in_str = 0; + while (*p) { + char c = *p; + if (in_str) { + if (c == '\\' && p[1]) { p += 2; continue; } + if (c == '"') in_str = 0; + } else { + if (c == '"') in_str = 1; + else if (c == '[') depth++; + else if (c == ']') { depth--; if (depth == 0) { p++; break; } } + } + p++; + } + size_t alen = (size_t)(p - arr_start); + int match = (klen == tag_len) && html_iemem(k, tag, klen); + if (match) { + if (out_len) *out_len = alen; + return arr_start; + } + } + return NULL; +} + +/* Returns 1 iff `attr` (length attr_len) appears as a string element + * in the JSON array slice [arr, arr+arr_len). Comparison is case- + * insensitive. */ +static int html_attr_in_array(const char* arr, size_t arr_len, + const char* attr, size_t attr_len) { + if (!arr || arr_len < 2) return 0; + const char* p = arr + 1; + const char* end = arr + arr_len - 1; + while (p < end) { + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',')) p++; + if (p >= end) return 0; + if (*p != '"') return 0; + p++; + const char* s = p; + while (p < end && *p != '"') { + if (*p == '\\' && p + 1 < end) p++; + p++; + } + if (p >= end) return 0; + size_t slen = (size_t)(p - s); + p++; + if (slen == attr_len && html_iemem(s, attr, slen)) return 1; + } + return 0; +} + +/* Hard-coded set of tags whose content is ALSO dropped (entire subtree). */ +static int html_is_dangerous_container(const char* tag, size_t tag_len) { + static const char* names[] = { + "script", "style", "iframe", "object", "embed", "form", + "noscript", "noembed", "template", "svg", "math", "frame", + "frameset", "applet", "audio", "video", "source", "track", + NULL + }; + for (int i = 0; names[i]; i++) { + if (html_ieq_n(tag, tag_len, names[i])) return 1; + } + return 0; +} + +/* HTML void elements — emit without a close tag. */ +static int html_is_void(const char* tag, size_t tag_len) { + static const char* names[] = { + "area", "base", "br", "col", "embed", "hr", "img", "input", + "link", "meta", "param", "source", "track", "wbr", + NULL + }; + for (int i = 0; names[i]; i++) { + if (html_ieq_n(tag, tag_len, names[i])) return 1; + } + return 0; +} + +/* Append a single byte HTML-escaped into the output buffer. */ +static void html_escape_byte(html_buf_t* out, unsigned char c) { + switch (c) { + case '<': html_buf_puts(out, "<"); break; + case '>': html_buf_puts(out, ">"); break; + case '"': html_buf_puts(out, """); break; + case '\'': html_buf_puts(out, "'"); break; + default: html_buf_putc(out, (char)c); break; + } +} + +/* Validate a URL value against the allowlist of safe schemes for hrefs. + * Returns 1 iff the URL is safe to emit. Acceptable forms: + * - http:// or https:// (case-insensitive) + * - mailto: + * - fragment-only `#anchor` + * - relative path that does not contain a colon before the first + * slash/?/# (so `foo/bar`, `/foo`, `?x=1` are OK; `javascript:x` is + * not — its colon precedes any path/hash/query separator). + * + * URL leading whitespace and embedded ASCII control bytes (TAB, LF, CR) + * are stripped before the scheme test, mirroring how browsers normalise + * URLs (these bytes are otherwise a known XSS bypass: `java\tscript:`). */ +static int html_url_is_safe(const char* url, size_t len) { + if (!url || len == 0) return 1; /* empty href is harmless */ + size_t i = 0; + while (i < len) { + unsigned char c = (unsigned char)url[i]; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == 0x0B || c == 0x0C) { + i++; continue; + } + break; + } + if (i >= len) return 1; /* whitespace only */ + if (url[i] == '#') return 1; /* fragment only */ + if (url[i] == '/' || url[i] == '?') return 1; /* relative */ + /* Find the first scheme-terminating character. */ + size_t scheme_end = (size_t)-1; + for (size_t j = i; j < len; j++) { + char c = url[j]; + if (c == ':') { scheme_end = j; break; } + if (c == '/' || c == '?' || c == '#') break; + } + if (scheme_end == (size_t)-1) return 1; /* no colon → relative path */ + /* Lowercase the scheme, stripping embedded control bytes. */ + char scheme[32]; + size_t sl = 0; + for (size_t j = i; j < scheme_end && sl < sizeof(scheme) - 1; j++) { + unsigned char c = (unsigned char)url[j]; + if (c == '\t' || c == '\n' || c == '\r' || c == 0x0B || c == 0x0C) continue; + scheme[sl++] = (char)html_tolower(c); + } + scheme[sl] = '\0'; + if (strcmp(scheme, "http") == 0) return 1; + if (strcmp(scheme, "https") == 0) return 1; + if (strcmp(scheme, "mailto") == 0) return 1; + return 0; +} + +el_val_t el_html_sanitize(el_val_t input_v, el_val_t allowlist_v) { + const char* input = EL_CSTR(input_v); + const char* allow = EL_CSTR(allowlist_v); + if (!input) return el_wrap_str(el_strdup("")); + if (!allow) allow = "{}"; + size_t in_len = strlen(input); + + html_buf_t out; + html_buf_init(&out); + + size_t i = 0; + while (i < in_len) { + unsigned char c = (unsigned char)input[i]; + if (c != '<') { + /* Plain text — escape and emit. We pass `&` through verbatim + * to preserve pre-encoded entities (`<`, `&`, `&#x...;`) + * which the browser will decode safely. */ + if (c == '&') html_buf_putc(&out, '&'); + else html_escape_byte(&out, c); + i++; + continue; + } + /* `<` — try to parse a tag. */ + if (i + 1 >= in_len) { + html_buf_puts(&out, "<"); + i++; + continue; + } + /* Comments, doctype, CDATA, processing instructions — drop entirely. */ + if (input[i + 1] == '!') { + if (i + 3 < in_len && input[i + 2] == '-' && input[i + 3] == '-') { + size_t j = i + 4; + while (j + 2 < in_len && !(input[j] == '-' && input[j + 1] == '-' && input[j + 2] == '>')) j++; + if (j + 2 < in_len) i = j + 3; + else i = in_len; + continue; + } + size_t j = i + 2; + while (j < in_len && input[j] != '>') j++; + i = (j < in_len) ? j + 1 : in_len; + continue; + } + if (input[i + 1] == '?') { + size_t j = i + 2; + while (j < in_len && input[j] != '>') j++; + i = (j < in_len) ? j + 1 : in_len; + continue; + } + int is_close = 0; + size_t name_start = i + 1; + if (input[i + 1] == '/') { + is_close = 1; + name_start = i + 2; + } + if (name_start >= in_len) { + html_buf_puts(&out, "<"); + i++; + continue; + } + unsigned char nc = (unsigned char)input[name_start]; + if (!((nc >= 'a' && nc <= 'z') || (nc >= 'A' && nc <= 'Z'))) { + /* `<` followed by non-letter — emit as escaped text. */ + html_buf_puts(&out, "<"); + i++; + continue; + } + size_t name_end = name_start; + while (name_end < in_len) { + unsigned char x = (unsigned char)input[name_end]; + if ((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || + (x >= '0' && x <= '9') || x == '-' || x == '_' || x == ':') { + name_end++; + } else { + break; + } + } + const char* tag = input + name_start; + size_t tag_len = name_end - name_start; + /* Find the `>` that closes this tag, respecting quoted attrs. */ + size_t cur = name_end; + int self_close = 0; + while (cur < in_len) { + unsigned char x = (unsigned char)input[cur]; + if (x == '"' || x == '\'') { + unsigned char q = x; + cur++; + while (cur < in_len && (unsigned char)input[cur] != q) cur++; + if (cur < in_len) cur++; /* skip closing quote */ + continue; + } + if (x == '/' && cur + 1 < in_len && input[cur + 1] == '>') { + self_close = 1; + break; + } + if (x == '>') break; + cur++; + } + if (cur >= in_len) { + /* Malformed: unclosed tag at EOF. Drop the rest of the input. */ + i = in_len; + continue; + } + size_t tag_end = self_close ? cur + 2 : cur + 1; /* one past `>` */ + /* Dangerous container — drop the whole subtree. */ + if (!is_close && html_is_dangerous_container(tag, tag_len)) { + if (self_close || html_is_void(tag, tag_len)) { + i = tag_end; + continue; + } + size_t scan = tag_end; + int found_close = 0; + while (scan < in_len) { + if (input[scan] != '<') { scan++; continue; } + if (scan + 1 < in_len && input[scan + 1] == '/') { + size_t cn_start = scan + 2; + size_t cn_end = cn_start; + while (cn_end < in_len) { + unsigned char x = (unsigned char)input[cn_end]; + if ((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || + (x >= '0' && x <= '9') || x == '-' || x == '_' || x == ':') { + cn_end++; + } else break; + } + if (cn_end - cn_start == tag_len && + html_iemem(input + cn_start, tag, tag_len)) { + size_t end_close = cn_end; + while (end_close < in_len && input[end_close] != '>') end_close++; + i = (end_close < in_len) ? end_close + 1 : in_len; + found_close = 1; + break; + } + } + scan++; + } + if (!found_close) { + /* No matching close — drop everything from here on. */ + i = in_len; + } + continue; + } + /* Look up the tag in the allowlist. */ + size_t arr_len = 0; + const char* arr = html_allowlist_find(allow, tag, tag_len, &arr_len); + if (!arr) { + /* Tag not allowed. Drop the open/close marker; inner text is + * processed by the outer loop and re-emitted as escaped text. */ + i = tag_end; + continue; + } + if (is_close) { + if (!html_is_void(tag, tag_len)) { + html_buf_putc(&out, '<'); + html_buf_putc(&out, '/'); + for (size_t k = 0; k < tag_len; k++) { + html_buf_putc(&out, (char)html_tolower((unsigned char)tag[k])); + } + html_buf_putc(&out, '>'); + } + i = tag_end; + continue; + } + /* Allowed open tag. Emit ``. */ + html_buf_putc(&out, '<'); + for (size_t k = 0; k < tag_len; k++) { + html_buf_putc(&out, (char)html_tolower((unsigned char)tag[k])); + } + size_t a = name_end; + while (a < cur) { + unsigned char x = (unsigned char)input[a]; + if (x == ' ' || x == '\t' || x == '\n' || x == '\r' || x == '/') { a++; continue; } + size_t an_start = a; + while (a < cur) { + unsigned char y = (unsigned char)input[a]; + if (y == '=' || y == ' ' || y == '\t' || y == '\n' || y == '\r' || y == '/' || y == '>') break; + a++; + } + size_t an_len = a - an_start; + if (an_len == 0) { a++; continue; } + size_t av_start = 0; + size_t av_len = 0; + int has_value = 0; + size_t b = a; + while (b < cur && (input[b] == ' ' || input[b] == '\t' || input[b] == '\n' || input[b] == '\r')) b++; + if (b < cur && input[b] == '=') { + has_value = 1; + b++; + while (b < cur && (input[b] == ' ' || input[b] == '\t' || input[b] == '\n' || input[b] == '\r')) b++; + if (b < cur && (input[b] == '"' || input[b] == '\'')) { + unsigned char q = (unsigned char)input[b]; + b++; + av_start = b; + while (b < cur && (unsigned char)input[b] != q) b++; + av_len = b - av_start; + if (b < cur) b++; + } else { + av_start = b; + while (b < cur) { + unsigned char y = (unsigned char)input[b]; + if (y == ' ' || y == '\t' || y == '\n' || y == '\r' || y == '>') break; + b++; + } + av_len = b - av_start; + } + a = b; + } + if (!html_attr_in_array(arr, arr_len, input + an_start, an_len)) continue; + int is_href = (an_len == 4 && html_iemem(input + an_start, "href", 4)); + int is_src = (an_len == 3 && html_iemem(input + an_start, "src", 3)); + if ((is_href || is_src) && has_value) { + if (!html_url_is_safe(input + av_start, av_len)) continue; + } + html_buf_putc(&out, ' '); + for (size_t k = 0; k < an_len; k++) { + html_buf_putc(&out, (char)html_tolower((unsigned char)input[an_start + k])); + } + if (has_value) { + html_buf_puts(&out, "=\""); + for (size_t k = 0; k < av_len; k++) { + unsigned char y = (unsigned char)input[av_start + k]; + /* Re-escape so the emitted attribute is well-formed + * double-quoted HTML. `&` passes through to preserve + * pre-encoded entities. */ + if (y == '"') html_buf_puts(&out, """); + else if (y == '<') html_buf_puts(&out, "<"); + else if (y == '>') html_buf_puts(&out, ">"); + else html_buf_putc(&out, (char)y); + } + html_buf_putc(&out, '"'); + } + } + html_buf_putc(&out, '>'); + i = tag_end; + } + /* Copy into arena-tracked buffer so the standard runtime memory model + * applies to the returned string. */ + char* result = el_strbuf(out.len); + memcpy(result, out.data, out.len); + result[out.len] = '\0'; + html_buf_free(&out); + return el_wrap_str(result); +} + /* ── 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); - /* Use a stack buffer for the pattern to avoid arena double-free. - * Keys in El maps are typically short; 512 bytes is a safe upper bound. */ - char stack_pat[512]; - char* pattern; - if (klen + 5 <= sizeof(stack_pat)) { - pattern = stack_pat; - } else { - pattern = malloc(klen + 5); - if (!pattern) return el_wrap_str(el_strdup("")); +/* True iff the segment is non-empty and every byte is an ASCII digit. We treat + * such segments as numeric array indices when walking a dot-path; mixed names + * like "0a" remain object-key lookups, so a key named "0" still wins over an + * index when the surrounding container is an object. */ +static int json_path_seg_is_index(const char* seg, size_t n) { + if (n == 0) return 0; + for (size_t i = 0; i < n; i++) { + if (seg[i] < '0' || seg[i] > '9') return 0; } - snprintf(pattern, klen + 5, "\"%s\":", key); - const char* p = strstr(json, pattern); - if (pattern != stack_pat) free(pattern); - if (!p) return el_wrap_str(el_strdup("")); - p += strlen(key) + 3; /* skip "key": */ - while (*p == ' ' || *p == '\t' || *p == '\n') p++; + return 1; +} + +/* Skip JSON whitespace. */ +static const char* json_skip_ws(const char* p) { + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; + return p; +} + +/* Descend one segment into the JSON cursor `p`. + * - If `p` points at an array `[...]` and the segment is all digits, + * advance to that element (zero-based). + * - Otherwise treat the segment as an object key and use json_find_key + * scoped to a one-level slice of the current container. + * Returns NULL if the descent fails (segment not found, container mismatch). + * + * `seg` is a pointer into the original path string and `seg_len` is its + * byte length — this avoids an extra alloc per segment. */ +static const char* json_path_descend(const char* p, const char* seg, size_t seg_len) { + if (!p || !seg) return NULL; + p = json_skip_ws(p); + if (*p == '[' && json_path_seg_is_index(seg, seg_len)) { + long idx = 0; + for (size_t i = 0; i < seg_len; i++) idx = idx * 10 + (seg[i] - '0'); + p++; /* step past '[' */ + p = json_skip_ws(p); + long cur = 0; + while (*p && *p != ']') { + if (cur == idx) return p; + const char* end = json_skip_value(p); + if (!end || end == p) return NULL; + p = json_skip_ws(end); + if (*p == ',') { p++; p = json_skip_ws(p); cur++; continue; } + /* No comma after this element — only acceptable at the closing ']', + * which means we ran out of elements. */ + break; + } + return NULL; + } + /* Object lookup. json_find_key walks at depth 1 of whatever container it + * receives, so we slice from `p` onwards. Caller already positioned us at + * the opening '{' (or at whitespace before it). */ + if (*p != '{') return NULL; + /* Build a NUL-terminated copy of the key segment for the lookup. We only + * pay this cost when the segment isn't a numeric index. */ + char stack_key[256]; + char* k = stack_key; + if (seg_len + 1 > sizeof(stack_key)) { + k = malloc(seg_len + 1); + if (!k) return NULL; + } + memcpy(k, seg, seg_len); + k[seg_len] = '\0'; + const char* found = json_find_key(p, k); + if (k != stack_key) free(k); + return found; +} + +/* Read the JSON value at `p` into a freshly-allocated, arena-owned el_val_t. + * - String -> unescaped, wrapped el_val_t string + * - Anything else -> raw JSON slice as a string (matches the historical + * json_get behaviour: numbers/bools/null come back stringified). */ +static el_val_t json_read_value(const char* p) { + p = json_skip_ws(p); if (*p == '"') { p++; - /* Unescape the JSON string value into a clean buffer. */ size_t cap = strlen(p) + 1; char* out = el_strbuf(cap); char* w = out; @@ -1920,15 +2672,70 @@ el_val_t json_get(el_val_t jsonv, el_val_t keyv) { *w = '\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'; + /* Object/array/number/bool/null — return the raw slice up to the value's + * end. json_skip_value tracks brace/bracket/string state so nested objects + * round-trip cleanly. */ + const char* end = json_skip_value(p); + if (!end) end = p; + size_t n = (size_t)(end - p); + /* Strip trailing whitespace from scalar values so callers don't see + * `123 ` when they parsed a pretty-printed number. */ + while (n > 0 && (p[n-1] == ' ' || p[n-1] == '\t' || p[n-1] == '\n' || p[n-1] == '\r')) { + n--; + } + char* out = el_strbuf(n); + memcpy(out, p, n); + out[n] = '\0'; return el_wrap_str(out); } +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("")); + + /* Fast path: key contains no '.' — keep the historical single-segment + * substring search so existing callers retain their O(strlen) cost + * profile. The dot-path walker is only paid for when needed. */ + if (!strchr(key, '.')) { + size_t klen = strlen(key); + char stack_pat[512]; + char* pattern; + if (klen + 5 <= sizeof(stack_pat)) { + pattern = stack_pat; + } else { + pattern = malloc(klen + 5); + if (!pattern) return el_wrap_str(el_strdup("")); + } + snprintf(pattern, klen + 5, "\"%s\":", key); + const char* p = strstr(json, pattern); + if (pattern != stack_pat) free(pattern); + if (!p) return el_wrap_str(el_strdup("")); + p += strlen(key) + 3; /* skip "key": */ + return json_read_value(p); + } + + /* Dot-path traversal. Walk segments left to right; at each step, descend + * into the current container by either array index (all-digit segment on + * an array cursor) or object key. */ + const char* cursor = json_skip_ws(json); + const char* seg_start = key; + const char* k = key; + while (1) { + if (*k == '.' || *k == '\0') { + size_t seg_len = (size_t)(k - seg_start); + cursor = json_path_descend(cursor, seg_start, seg_len); + if (!cursor) return el_wrap_str(el_strdup("")); + if (*k == '\0') break; + k++; + seg_start = k; + continue; + } + k++; + } + return json_read_value(cursor); +} + /* ── Float bit-cast helpers ──────────────────────────────────────────────── */ /* `el_to_float` and `el_from_float` are exposed in el_runtime.h as static * inlines so generated programs (which #include the header) can call them @@ -2188,8 +2995,13 @@ static int looks_like_string(el_val_t v) { const unsigned char* s = (const unsigned char*)p; for (int i = 0; i < 16; i++) { unsigned char c = s[i]; - if (c == '\0') return i > 0; /* terminated string */ - if (c < 0x09 || (c > 0x0d && c < 0x20) || c >= 0x7f) return 0; + if (c == '\0') return 1; /* terminated string (empty string is still a valid string) */ + /* Reject C0 control chars (non-whitespace), allow UTF-8 high bytes. + * 0x09-0x0d = tab/newline/cr/vt/ff (whitespace, OK) + * 0x20-0x7e = printable ASCII (OK) + * 0x7f = DEL (reject) + * 0x80-0xff = UTF-8 continuation/lead bytes (OK for multi-byte chars) */ + if (c < 0x09 || (c > 0x0d && c < 0x20) || c == 0x7f) return 0; } return 1; /* 16+ printable bytes — call it a string */ } @@ -2371,6 +3183,9 @@ el_val_t json_get_raw(el_val_t json_str, el_val_t key) { const char* json = EL_CSTR(json_str); const char* k = EL_CSTR(key); const char* p = json_find_key(json, k); + /* Clear fs_read binary-length hint — result is a fresh null-terminated + * string, not the raw file bytes, so Content-Length must use strlen. */ + _tl_fs_read_len = 0; if (!p) return el_wrap_str(el_strdup("")); const char* end = json_skip_value(p); size_t n = (size_t)(end - p); @@ -2624,6 +3439,1155 @@ el_val_t sleep_ms(el_val_t ms) { return 0; } +/* ── Instant + Duration: first-class temporal types ────────────────────────── + * El's substrate (Neuron) is a temporal cognition system. Memory salience + * decay, the six-tier pacemaker, TTL caches, and supersession are all + * temporal. Treating time as a raw Int (now() returning ms-since-epoch and + * arithmetic done with mixed unit literals) lets bugs through the type + * system: `(now - cached_at) < 60` cannot tell ms from sec, and `sleep(30)` + * is ambiguous. This block introduces two dedicated representations. + * + * Representation: + * Instant — int64 nanoseconds since the Unix epoch + * Duration — int64 nanoseconds (signed; negative durations are legal, + * e.g. when a deadline has passed) + * + * Both share the el_val_t (int64) slot the rest of the runtime uses, so no + * boxing / arena allocation is needed. Type discipline is enforced at the + * codegen layer: `let x: Duration = ...` registers `x` in __duration_names, + * and BinOp dispatches through typed wrappers (el_duration_add, etc.) that + * make intent explicit in the generated C. Mismatched ops (Instant+Instant, + * Duration+Int) are surfaced via #error directives at codegen time so the + * downstream cc step fails with a clear El-source-level message. + * + * Nanosecond precision matches POSIX clock_gettime / nanosleep granularity. + * 2^63 nanos covers ~292 years from epoch — comfortably past 2200, plenty + * for a memory-system runtime that never schedules outside a human lifespan. + */ + +/* now() — current Instant. Wraps clock_gettime(CLOCK_REALTIME) for nanosecond + * precision. Falls back to gettimeofday on systems where clock_gettime is + * unavailable (defensive — every supported platform has it). */ +el_val_t el_now_instant(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); + int64_t ns = (int64_t)tv.tv_sec * 1000000000LL + + (int64_t)tv.tv_usec * 1000LL; + return (el_val_t)ns; +} + +el_val_t now(void) { + return el_now_instant(); +} + +/* unix_seconds(n) — Instant from a Unix-epoch second count. + * unix_millis(n) — Instant from a Unix-epoch millisecond count. */ +el_val_t unix_seconds(el_val_t n) { + int64_t s = (int64_t)n; + return (el_val_t)(s * 1000000000LL); +} + +el_val_t unix_millis(el_val_t n) { + int64_t m = (int64_t)n; + return (el_val_t)(m * 1000000LL); +} + +/* instant_from_iso8601 — parse a strict subset: + * YYYY-MM-DDTHH:MM:SS[.fff]Z + * Returns 0 (the Unix-epoch sentinel) on parse failure. Callers that need to + * distinguish epoch-zero from a parse error should use a wider sentinel + * representation; the current zero-on-failure choice matches existing El + * runtime conventions for parse builtins (str_to_int, parse_int). */ +el_val_t instant_from_iso8601(el_val_t s) { + const char* str = EL_CSTR(s); + if (!str) return (el_val_t)0; + int Y, M, D, h, m, sec, frac = 0; + int n = sscanf(str, "%d-%d-%dT%d:%d:%d.%3d", &Y, &M, &D, &h, &m, &sec, &frac); + if (n < 6) { + n = sscanf(str, "%d-%d-%dT%d:%d:%dZ", &Y, &M, &D, &h, &m, &sec); + if (n < 6) return (el_val_t)0; + } + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = Y - 1900; + tm.tm_mon = M - 1; + tm.tm_mday = D; + tm.tm_hour = h; + tm.tm_min = m; + tm.tm_sec = sec; + /* timegm — UTC. POSIX-Y but available on macOS and glibc. */ + time_t t = timegm(&tm); + if (t == (time_t)-1) return (el_val_t)0; + int64_t ns = (int64_t)t * 1000000000LL + (int64_t)frac * 1000000LL; + return (el_val_t)ns; +} + +/* Duration constructors. The El-side postfix literals (30.seconds, 1.hour) + * are lowered by the codegen directly into a literal int64 of nanoseconds — + * these constructors are for runtime values where the count is dynamic. */ +el_val_t el_duration_from_nanos(el_val_t ns) { + return (el_val_t)(int64_t)ns; +} + +el_val_t duration_seconds(el_val_t n) { + int64_t s = (int64_t)n; + return (el_val_t)(s * 1000000000LL); +} + +el_val_t duration_millis(el_val_t n) { + int64_t m = (int64_t)n; + return (el_val_t)(m * 1000000LL); +} + +el_val_t duration_nanos(el_val_t n) { + return (el_val_t)(int64_t)n; +} + +/* Arithmetic — typed wrappers. At the C level these are no-op casts, but + * the codegen routes Instant/Duration BinOps through them so the generated + * C says `el_instant_add_dur(start, dur)` rather than `start + dur`. The + * intent is explicit, the operand order is documented, and a future change + * to the underlying representation (saturating arithmetic, overflow guards) + * has a single chokepoint. */ +el_val_t el_instant_add_dur(el_val_t inst, el_val_t dur) { + return (el_val_t)((int64_t)inst + (int64_t)dur); +} + +el_val_t el_instant_sub_dur(el_val_t inst, el_val_t dur) { + return (el_val_t)((int64_t)inst - (int64_t)dur); +} + +el_val_t el_instant_diff(el_val_t a, el_val_t b) { + /* a - b — yields a Duration (negative if b is later than a). */ + return (el_val_t)((int64_t)a - (int64_t)b); +} + +el_val_t el_duration_add(el_val_t a, el_val_t b) { + return (el_val_t)((int64_t)a + (int64_t)b); +} + +el_val_t el_duration_sub(el_val_t a, el_val_t b) { + return (el_val_t)((int64_t)a - (int64_t)b); +} + +el_val_t el_duration_scale(el_val_t dur, el_val_t scalar) { + return (el_val_t)((int64_t)dur * (int64_t)scalar); +} + +el_val_t el_duration_div(el_val_t dur, el_val_t scalar) { + int64_t s = (int64_t)scalar; + if (s == 0) return (el_val_t)0; + return (el_val_t)((int64_t)dur / s); +} + +/* Comparisons. Return 1/0 in el_val_t convention. */ +el_val_t el_instant_lt(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a < (int64_t)b ? 1 : 0); } +el_val_t el_instant_le(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a <= (int64_t)b ? 1 : 0); } +el_val_t el_instant_gt(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a > (int64_t)b ? 1 : 0); } +el_val_t el_instant_ge(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a >= (int64_t)b ? 1 : 0); } +el_val_t el_instant_eq(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a == (int64_t)b ? 1 : 0); } +el_val_t el_instant_ne(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a != (int64_t)b ? 1 : 0); } +el_val_t el_duration_lt(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a < (int64_t)b ? 1 : 0); } +el_val_t el_duration_le(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a <= (int64_t)b ? 1 : 0); } +el_val_t el_duration_gt(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a > (int64_t)b ? 1 : 0); } +el_val_t el_duration_ge(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a >= (int64_t)b ? 1 : 0); } +el_val_t el_duration_eq(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a == (int64_t)b ? 1 : 0); } +el_val_t el_duration_ne(el_val_t a, el_val_t b) { return (el_val_t)((int64_t)a != (int64_t)b ? 1 : 0); } + +/* Conversions. */ +el_val_t instant_to_unix_seconds(el_val_t i) { + return (el_val_t)((int64_t)i / 1000000000LL); +} + +el_val_t instant_to_unix_millis(el_val_t i) { + return (el_val_t)((int64_t)i / 1000000LL); +} + +el_val_t instant_to_iso8601(el_val_t i) { + int64_t ns = (int64_t)i; + time_t s = (time_t)(ns / 1000000000LL); + int msec = (int)((ns / 1000000LL) % 1000LL); + if (msec < 0) { msec += 1000; s -= 1; } + struct tm tm; + gmtime_r(&s, &tm); + char buf[64]; + snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, msec); + return el_wrap_str(el_strdup(buf)); +} + +el_val_t duration_to_seconds(el_val_t d) { + return (el_val_t)((int64_t)d / 1000000000LL); +} + +el_val_t duration_to_millis(el_val_t d) { + return (el_val_t)((int64_t)d / 1000000LL); +} + +el_val_t duration_to_nanos(el_val_t d) { + return (el_val_t)(int64_t)d; +} + +/* sleep(Duration) — Phase 1 replacement for ambiguous sleep(Int). The runtime + * still exposes sleep_secs/sleep_ms for legacy call sites; codegen lowers + * sleep(Duration) to el_sleep_duration(d). Negative durations clamp to 0 so a + * stale deadline doesn't block forever. */ +el_val_t el_sleep_duration(el_val_t dur) { + int64_t ns = (int64_t)dur; + if (ns < 0) ns = 0; + struct timespec ts; + ts.tv_sec = (time_t)(ns / 1000000000LL); + ts.tv_nsec = (long)(ns % 1000000000LL); + nanosleep(&ts, NULL); + return (el_val_t)0; +} + +/* unix_timestamp() — back-compat. Existing El callers expect an Int seconds + * value; this stays an Int returner so the type system isn't disturbed for + * legacy code. New code should call now() and convert when needed. */ +el_val_t unix_timestamp(void) { + return instant_to_unix_seconds(el_now_instant()); +} + +/* TTL cache helpers. Backed by the existing process-wide K/V (state_set/get) + * with a sibling __ttl_set_at_ entry recording the Instant of the last + * write. ttl_cache_get returns "" if the entry is missing or stale, so call + * sites can branch on `if v == "" { miss } else { hit }` — the same shape + * existing get-with-default code uses. No more (now - cached_at) < 60. */ +el_val_t ttl_cache_set(el_val_t key, el_val_t value) { + const char* k = EL_CSTR(key); + if (!k) return (el_val_t)0; + /* Store the value at the user's key. */ + state_set(key, value); + /* Stamp set_at — opaque schema, namespaced under __ttl: prefix so user + * keys can't collide with stamps. */ + size_t klen = strlen(k); + char* stamp_key = (char*)malloc(klen + 16); + if (!stamp_key) return (el_val_t)0; + snprintf(stamp_key, klen + 16, "__ttl_at:%s", k); + int64_t now_ns = (int64_t)el_now_instant(); + char buf[32]; + snprintf(buf, sizeof(buf), "%lld", (long long)now_ns); + state_set(EL_STR(stamp_key), EL_STR(buf)); + free(stamp_key); + return (el_val_t)1; +} + +el_val_t ttl_cache_get(el_val_t key, el_val_t max_age) { + const char* k = EL_CSTR(key); + if (!k) return el_wrap_str(el_strdup("")); + /* Look up stamp. */ + size_t klen = strlen(k); + char* stamp_key = (char*)malloc(klen + 16); + if (!stamp_key) return el_wrap_str(el_strdup("")); + snprintf(stamp_key, klen + 16, "__ttl_at:%s", k); + el_val_t stamp = state_get(EL_STR(stamp_key)); + free(stamp_key); + const char* sv = EL_CSTR(stamp); + if (!sv || !*sv) return el_wrap_str(el_strdup("")); + int64_t set_at = (int64_t)atoll(sv); + int64_t now_ns = (int64_t)el_now_instant(); + int64_t age = now_ns - set_at; + int64_t max_ns = (int64_t)max_age; + if (age < 0) return el_wrap_str(el_strdup("")); /* clock skew — treat as miss */ + if (age > max_ns) return el_wrap_str(el_strdup("")); /* expired */ + return state_get(key); +} + +el_val_t ttl_cache_age(el_val_t key) { + const char* k = EL_CSTR(key); + if (!k) return (el_val_t)INT64_MAX; + size_t klen = strlen(k); + char* stamp_key = (char*)malloc(klen + 16); + if (!stamp_key) return (el_val_t)INT64_MAX; + snprintf(stamp_key, klen + 16, "__ttl_at:%s", k); + el_val_t stamp = state_get(EL_STR(stamp_key)); + free(stamp_key); + const char* sv = EL_CSTR(stamp); + if (!sv || !*sv) return (el_val_t)INT64_MAX; + int64_t set_at = (int64_t)atoll(sv); + int64_t now_ns = (int64_t)el_now_instant(); + return (el_val_t)(now_ns - set_at); +} + +/* ── Calendar + CalendarTime + Rhythm + LocalDate/Time/DateTime ────────────── + * Phase 1.5. Calendar is pluggable: EarthCalendar (IANA zones + Gregorian + + * DST), MarsCalendar (sols, MTC), CycleCalendar(period), NoCycleCalendar, + * RelativeCalendar(epoch). Phase 1 zone wrapping folds INTO EarthCalendar; + * UTC and IANA zones are themselves Earth-parochial and cannot live at the + * lowest type layer. + * + * A Rhythm is a small AST that asks the Calendar for cycle phase, weekday, + * etc. Most rhythm logic is calendar-agnostic at runtime: rhythm_cycle_phase + * means "midpoint of cycle" whether the cycle is 24h on Earth or 30h on a + * station or 300y on a long-cycle world. */ + +/* Magic headers — used by the runtime to recognize boxed temporal values + * arriving through el_val_t. Distinct constants so accidental misuse fails + * loudly rather than silently. */ +#define EL_CAL_MAGIC 0xE1CA1EDDU +#define EL_CALTIME_MAGIC 0xE1CA1747U +#define EL_RHYTHM_MAGIC 0xE1287A11U +#define EL_LDATE_MAGIC 0xE1DA7E00U +#define EL_LDT_MAGIC 0xE1DA7E1DU +#define EL_ZONE_MAGIC 0xE12017E0U + +typedef enum { + EL_CALENDAR_EARTH = 1, + EL_CALENDAR_MARS = 2, + EL_CALENDAR_CYCLE = 3, + EL_CALENDAR_NO_CYCLE = 4, + EL_CALENDAR_RELATIVE = 5 +} el_calendar_kind_t; + +typedef struct { + uint32_t magic; + char* id; /* IANA name or "+HH:MM" / "-HH:MM" */ + int fixed; /* 1 for fixed offset, 0 for IANA */ + int64_t offset_ns; /* fixed offset in nanos (only when fixed) */ +} el_zone_t; + +typedef struct { + uint32_t magic; + el_calendar_kind_t kind; + el_zone_t* zone; /* EarthCalendar; MarsCalendar uses MTC */ + int64_t cycle_period_ns;/* CycleCalendar; computed for Earth (86400 s) and Mars (88775.244 s) */ + int64_t epoch_ns; /* RelativeCalendar; Unix-epoch zero otherwise */ +} el_calendar_t; + +typedef struct { + uint32_t magic; + int64_t instant_ns; + el_calendar_t* cal; +} el_caltime_t; + +/* Rhythm AST. */ +typedef enum { + EL_RHYTHM_CYCLE_START = 1, + EL_RHYTHM_CYCLE_PHASE = 2, + EL_RHYTHM_DURATION = 3, + EL_RHYTHM_SESSION_START = 4, + EL_RHYTHM_EVENT = 5, + EL_RHYTHM_AND = 6, + EL_RHYTHM_OR = 7, + EL_RHYTHM_WEEKDAY = 8, + EL_RHYTHM_WEEKLY_AT = 9 +} el_rhythm_kind_t; + +typedef struct el_rhythm_s { + uint32_t magic; + el_rhythm_kind_t kind; + double phase; /* CYCLE_PHASE */ + int64_t period_ns; /* DURATION */ + int weekday; /* 1..7 Mon..Sun */ + int hour; + int minute; + char* event_name; /* EVENT */ + struct el_rhythm_s* a; /* AND/OR */ + struct el_rhythm_s* b; +} el_rhythm_t; + +typedef struct { + uint32_t magic; + int year; + int month; + int day; +} el_localdate_t; + +typedef struct { + uint32_t magic; + el_localdate_t* date; + int64_t time_ns; /* nanos since midnight */ +} el_localdt_t; + +/* Magic-tag check helpers — peek the first 4 bytes of an el_val_t pointer + * and compare against the expected magic. Strings are NUL-terminated and + * never start with our magic byte sequence, so this is safe. */ +static int el_is_magic(el_val_t v, uint32_t want) { + if (v == 0) return 0; + /* Defensive: only follow pointers in plausible address space. + * On 64-bit unix processes pointers are above 0x10000. */ + if ((uint64_t)v < 0x10000ULL) return 0; + uint32_t got = *(volatile uint32_t*)(uintptr_t)v; + return got == want; +} + +/* Sol length on Mars in nanoseconds: 88775.244 seconds. */ +#define EL_MARS_SOL_NS ((int64_t)88775244000000LL) +/* Earth solar day in nanoseconds: 86400 seconds. */ +#define EL_EARTH_DAY_NS ((int64_t)86400000000000LL) + +/* ── Zone construction ────────────────────────────────────────────────────── + * Zones intern by id string so equality comparisons are pointer-compares. */ + +#define EL_ZONE_TABLE_CAP 64 +static el_zone_t* _el_zone_table[EL_ZONE_TABLE_CAP]; +static int _el_zone_count = 0; + +static el_zone_t* _el_zone_intern(const char* id, int fixed, int64_t offset_ns) { + for (int i = 0; i < _el_zone_count; i++) { + el_zone_t* z = _el_zone_table[i]; + if (z->fixed == fixed && z->offset_ns == offset_ns && + strcmp(z->id ? z->id : "", id ? id : "") == 0) { + return z; + } + } + if (_el_zone_count >= EL_ZONE_TABLE_CAP) { + /* Out of slots: build a non-interned zone. Equality will fail across + * such zones but the program still runs. */ + el_zone_t* z = (el_zone_t*)malloc(sizeof(el_zone_t)); + z->magic = EL_ZONE_MAGIC; + z->id = el_strdup_persist(id ? id : ""); + z->fixed = fixed; + z->offset_ns = offset_ns; + return z; + } + el_zone_t* z = (el_zone_t*)malloc(sizeof(el_zone_t)); + z->magic = EL_ZONE_MAGIC; + z->id = el_strdup_persist(id ? id : ""); + z->fixed = fixed; + z->offset_ns = offset_ns; + _el_zone_table[_el_zone_count++] = z; + return z; +} + +el_val_t zone(el_val_t id) { + const char* s = EL_CSTR(id); + if (!s || !*s) return (el_val_t)(uintptr_t)_el_zone_intern("UTC", 0, 0); + /* Fixed-offset shortcut: "+HH:MM" or "-HH:MM". */ + if ((s[0] == '+' || s[0] == '-') && strlen(s) >= 6 && s[3] == ':') { + int sign = (s[0] == '-') ? -1 : 1; + int hh = (s[1] - '0') * 10 + (s[2] - '0'); + int mm = (s[4] - '0') * 10 + (s[5] - '0'); + int64_t off = (int64_t)sign * ((int64_t)hh * 3600LL + (int64_t)mm * 60LL) * 1000000000LL; + return (el_val_t)(uintptr_t)_el_zone_intern(s, 1, off); + } + return (el_val_t)(uintptr_t)_el_zone_intern(s, 0, 0); +} + +el_val_t zone_utc(void) { + return (el_val_t)(uintptr_t)_el_zone_intern("UTC", 1, 0); +} + +el_val_t zone_local(void) { + /* Resolve the local zone via TZ env or system default. tzset() picks + * up TZ if set; otherwise the C library reads /etc/localtime. We store + * the zone id as "LOCAL" so subsequent equality holds; resolution is + * lazy at use time. */ + return (el_val_t)(uintptr_t)_el_zone_intern("LOCAL", 0, 0); +} + +el_val_t zone_offset(el_val_t hours, el_val_t minutes) { + int hh = (int)(int64_t)hours; + int mm = (int)(int64_t)minutes; + int sign = (hh < 0 || mm < 0) ? -1 : 1; + if (hh < 0) hh = -hh; + if (mm < 0) mm = -mm; + int64_t off = (int64_t)sign * ((int64_t)hh * 3600LL + (int64_t)mm * 60LL) * 1000000000LL; + char buf[16]; + snprintf(buf, sizeof(buf), "%c%02d:%02d", sign < 0 ? '-' : '+', hh, mm); + return (el_val_t)(uintptr_t)_el_zone_intern(buf, 1, off); +} + +/* ── Calendar interning ──────────────────────────────────────────────────── */ + +#define EL_CAL_TABLE_CAP 64 +static el_calendar_t* _el_cal_table[EL_CAL_TABLE_CAP]; +static int _el_cal_count = 0; + +static el_calendar_t* _el_cal_intern(el_calendar_kind_t kind, el_zone_t* z, + int64_t period_ns, int64_t epoch_ns) { + for (int i = 0; i < _el_cal_count; i++) { + el_calendar_t* c = _el_cal_table[i]; + if (c->kind == kind && c->zone == z && + c->cycle_period_ns == period_ns && c->epoch_ns == epoch_ns) { + return c; + } + } + el_calendar_t* c = (el_calendar_t*)malloc(sizeof(el_calendar_t)); + c->magic = EL_CAL_MAGIC; + c->kind = kind; + c->zone = z; + c->cycle_period_ns = period_ns; + c->epoch_ns = epoch_ns; + if (_el_cal_count < EL_CAL_TABLE_CAP) _el_cal_table[_el_cal_count++] = c; + return c; +} + +el_val_t earth_calendar(el_val_t z_val) { + el_zone_t* z = NULL; + if (z_val != 0 && el_is_magic(z_val, EL_ZONE_MAGIC)) { + z = (el_zone_t*)(uintptr_t)z_val; + } else { + z = (el_zone_t*)(uintptr_t)zone_local(); + } + return (el_val_t)(uintptr_t)_el_cal_intern(EL_CALENDAR_EARTH, z, EL_EARTH_DAY_NS, 0); +} + +el_val_t earth_calendar_default(void) { + return earth_calendar(zone_local()); +} + +el_val_t mars_calendar(void) { + el_zone_t* z = (el_zone_t*)(uintptr_t)_el_zone_intern("MTC", 1, 0); + return (el_val_t)(uintptr_t)_el_cal_intern(EL_CALENDAR_MARS, z, EL_MARS_SOL_NS, 0); +} + +el_val_t cycle_calendar(el_val_t period_dur) { + int64_t period = (int64_t)period_dur; + if (period <= 0) period = 1; + return (el_val_t)(uintptr_t)_el_cal_intern(EL_CALENDAR_CYCLE, NULL, period, 0); +} + +el_val_t no_cycle_calendar(void) { + return (el_val_t)(uintptr_t)_el_cal_intern(EL_CALENDAR_NO_CYCLE, NULL, 0, 0); +} + +el_val_t relative_calendar(el_val_t epoch_inst) { + int64_t ep = (int64_t)epoch_inst; + return (el_val_t)(uintptr_t)_el_cal_intern(EL_CALENDAR_RELATIVE, NULL, 0, ep); +} + +/* ── CalendarTime ───────────────────────────────────────────────────────── */ + +static el_caltime_t* _el_caltime_alloc(int64_t inst, el_calendar_t* c) { + el_caltime_t* ct = (el_caltime_t*)malloc(sizeof(el_caltime_t)); + ct->magic = EL_CALTIME_MAGIC; + ct->instant_ns = inst; + ct->cal = c; + return ct; +} + +static el_calendar_t* _el_resolve_cal(el_val_t cal_val) { + if (cal_val == 0 || !el_is_magic(cal_val, EL_CAL_MAGIC)) { + return (el_calendar_t*)(uintptr_t)earth_calendar_default(); + } + return (el_calendar_t*)(uintptr_t)cal_val; +} + +el_val_t now_in(el_val_t cal_val) { + el_calendar_t* c = _el_resolve_cal(cal_val); + int64_t ns = (int64_t)el_now_instant(); + return (el_val_t)(uintptr_t)_el_caltime_alloc(ns, c); +} + +el_val_t in_calendar(el_val_t inst, el_val_t cal_val) { + el_calendar_t* c = _el_resolve_cal(cal_val); + return (el_val_t)(uintptr_t)_el_caltime_alloc((int64_t)inst, c); +} + +el_val_t cal_to_instant(el_val_t ct_val) { + if (!el_is_magic(ct_val, EL_CALTIME_MAGIC)) return (el_val_t)0; + el_caltime_t* ct = (el_caltime_t*)(uintptr_t)ct_val; + return (el_val_t)ct->instant_ns; +} + +el_val_t cal_in(el_val_t ct_val, el_val_t cal_val) { + if (!el_is_magic(ct_val, EL_CALTIME_MAGIC)) return (el_val_t)0; + el_caltime_t* ct = (el_caltime_t*)(uintptr_t)ct_val; + el_calendar_t* c = _el_resolve_cal(cal_val); + return (el_val_t)(uintptr_t)_el_caltime_alloc(ct->instant_ns, c); +} + +el_val_t cal_cycle_phase(el_val_t ct_val) { + if (!el_is_magic(ct_val, EL_CALTIME_MAGIC)) return el_from_float(0.0); + el_caltime_t* ct = (el_caltime_t*)(uintptr_t)ct_val; + el_calendar_t* c = ct->cal; + if (c->kind == EL_CALENDAR_NO_CYCLE) { + return el_from_float(0.0/0.0); /* NaN sentinel */ + } + int64_t period = c->cycle_period_ns; + if (period <= 0) return el_from_float(0.0); + int64_t base = ct->instant_ns - c->epoch_ns; + int64_t phase_ns = base % period; + if (phase_ns < 0) phase_ns += period; + double phase = (double)phase_ns / (double)period; + return el_from_float(phase); +} + +/* ── Earth zone resolution: TZ-based offset lookup ────────────────────────── + * For an EarthCalendar(zone), we want to convert an instant_ns into local + * y/m/d/h/m/s, including DST. Approach: setenv("TZ", id), tzset(), use + * localtime_r, then restore. This is not thread-safe by design — El's + * runtime is single-threaded for the request handler path. Cache the + * computed (instant -> tm) to avoid the syscall churn on repeat formats. */ + +static void _el_apply_zone(el_zone_t* z) { + if (!z) { unsetenv("TZ"); tzset(); return; } + if (z->fixed && strcmp(z->id, "UTC") == 0) { + setenv("TZ", "UTC0", 1); + tzset(); + return; + } + if (z->fixed) { + /* Fixed offset: POSIX TZ uses inverted sign (sign convention of + * "hours WEST of UTC" rather than east). Build the spec accordingly. */ + char buf[32]; + int neg_secs = (int)(-z->offset_ns / 1000000000LL); + int sign = neg_secs < 0 ? -1 : 1; + int abs_secs = neg_secs < 0 ? -neg_secs : neg_secs; + int hh = abs_secs / 3600; + int mm = (abs_secs % 3600) / 60; + snprintf(buf, sizeof(buf), "FIX%c%d:%02d", sign < 0 ? '-' : '+', hh, mm); + setenv("TZ", buf, 1); + tzset(); + return; + } + if (strcmp(z->id, "LOCAL") == 0) { + unsetenv("TZ"); + tzset(); + return; + } + setenv("TZ", z->id, 1); + tzset(); +} + +static int _el_decompose_earth(el_caltime_t* ct, struct tm* tm_out, int* abbr_len, char* abbr_buf, size_t abbr_cap) { + el_calendar_t* c = ct->cal; + el_zone_t* z = c->zone; + _el_apply_zone(z); + time_t s = (time_t)(ct->instant_ns / 1000000000LL); + struct tm tm; + localtime_r(&s, &tm); + *tm_out = tm; + if (abbr_buf && abbr_cap > 0) { + const char* z_str = tm.tm_zone ? tm.tm_zone : ""; + size_t n = strlen(z_str); + if (n >= abbr_cap) n = abbr_cap - 1; + memcpy(abbr_buf, z_str, n); + abbr_buf[n] = '\0'; + if (abbr_len) *abbr_len = (int)n; + } + return 0; +} + +/* Format an Earth CalendarTime under a Java-DateTimeFormatter-ish pattern. + * We support a useful core: yyyy MM dd HH mm ss z EEE MMM d h a — enough for + * the acceptance tests. Single quotes denote literal text. */ +static const char* _el_weekday_short[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; +static const char* _el_month_short[] = {"Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec"}; + +static char* _el_format_earth(el_caltime_t* ct, const char* pattern) { + struct tm tm; + char abbr[16] = {0}; + int abbr_len = 0; + _el_decompose_earth(ct, &tm, &abbr_len, abbr, sizeof(abbr)); + size_t cap = strlen(pattern) * 4 + 64; + char* out = (char*)malloc(cap); + size_t pos = 0; + size_t i = 0; + size_t plen = strlen(pattern); + while (i < plen) { + char ch = pattern[i]; + /* Quoted literal */ + if (ch == '\'') { + i++; + while (i < plen && pattern[i] != '\'') { + if (pos + 1 >= cap) { cap *= 2; out = realloc(out, cap); } + out[pos++] = pattern[i++]; + } + if (i < plen) i++; + continue; + } + /* Count run of same letter */ + size_t run = 1; + while (i + run < plen && pattern[i + run] == ch) run++; + char tmp[64]; + tmp[0] = '\0'; + if (ch == 'y') { + if (run >= 4) snprintf(tmp, sizeof(tmp), "%04d", tm.tm_year + 1900); + else snprintf(tmp, sizeof(tmp), "%02d", (tm.tm_year + 1900) % 100); + } else if (ch == 'M') { + if (run >= 3) snprintf(tmp, sizeof(tmp), "%s", _el_month_short[tm.tm_mon]); + else if (run == 2) snprintf(tmp, sizeof(tmp), "%02d", tm.tm_mon + 1); + else snprintf(tmp, sizeof(tmp), "%d", tm.tm_mon + 1); + } else if (ch == 'd') { + if (run >= 2) snprintf(tmp, sizeof(tmp), "%02d", tm.tm_mday); + else snprintf(tmp, sizeof(tmp), "%d", tm.tm_mday); + } else if (ch == 'H') { + if (run >= 2) snprintf(tmp, sizeof(tmp), "%02d", tm.tm_hour); + else snprintf(tmp, sizeof(tmp), "%d", tm.tm_hour); + } else if (ch == 'h') { + int h12 = tm.tm_hour % 12; if (h12 == 0) h12 = 12; + if (run >= 2) snprintf(tmp, sizeof(tmp), "%02d", h12); + else snprintf(tmp, sizeof(tmp), "%d", h12); + } else if (ch == 'm') { + if (run >= 2) snprintf(tmp, sizeof(tmp), "%02d", tm.tm_min); + else snprintf(tmp, sizeof(tmp), "%d", tm.tm_min); + } else if (ch == 's') { + if (run >= 2) snprintf(tmp, sizeof(tmp), "%02d", tm.tm_sec); + else snprintf(tmp, sizeof(tmp), "%d", tm.tm_sec); + } else if (ch == 'a') { + snprintf(tmp, sizeof(tmp), "%s", tm.tm_hour < 12 ? "AM" : "PM"); + } else if (ch == 'E') { + snprintf(tmp, sizeof(tmp), "%s", _el_weekday_short[tm.tm_wday]); + } else if (ch == 'z') { + snprintf(tmp, sizeof(tmp), "%s", abbr); + } else { + for (size_t k = 0; k < run; k++) { + if (pos + 1 >= cap) { cap *= 2; out = realloc(out, cap); } + out[pos++] = ch; + } + i += run; + continue; + } + size_t tl = strlen(tmp); + if (pos + tl + 1 >= cap) { cap = (cap + tl) * 2; out = realloc(out, cap); } + memcpy(out + pos, tmp, tl); + pos += tl; + i += run; + } + out[pos] = '\0'; + char* result = el_strdup(out); + free(out); + return result; +} + +/* Format a Mars CalendarTime: %sol prints the integer sol number since + * mission epoch (Unix epoch fallback), %phase prints cycle_phase as a + * 0..1 decimal. Other %-specifiers fall through. */ +static char* _el_format_mars(el_caltime_t* ct, const char* pattern) { + el_calendar_t* c = ct->cal; + int64_t period = c->cycle_period_ns > 0 ? c->cycle_period_ns : EL_MARS_SOL_NS; + int64_t base = ct->instant_ns - c->epoch_ns; + int64_t sol = base / period; + int64_t phase_ns = base % period; + if (phase_ns < 0) { phase_ns += period; sol -= 1; } + double phase = (double)phase_ns / (double)period; + size_t cap = strlen(pattern) * 4 + 64; + char* out = (char*)malloc(cap); + size_t pos = 0; + for (size_t i = 0; pattern[i]; i++) { + if (pattern[i] == '%' && pattern[i+1]) { + char tmp[64]; + tmp[0] = '\0'; + if (strncmp(pattern + i + 1, "sol", 3) == 0) { + snprintf(tmp, sizeof(tmp), "%lld", (long long)sol); + i += 3; + } else if (strncmp(pattern + i + 1, "phase", 5) == 0) { + snprintf(tmp, sizeof(tmp), "%.4f", phase); + i += 5; + } else if (pattern[i+1] == 'd') { + snprintf(tmp, sizeof(tmp), "%lld", (long long)sol); + i += 1; + } else { + tmp[0] = pattern[i+1]; tmp[1] = '\0'; + i += 1; + } + size_t tl = strlen(tmp); + if (pos + tl + 1 >= cap) { cap = (cap + tl) * 2; out = realloc(out, cap); } + memcpy(out + pos, tmp, tl); + pos += tl; + } else { + if (pos + 1 >= cap) { cap *= 2; out = realloc(out, cap); } + out[pos++] = pattern[i]; + } + } + out[pos] = '\0'; + char* result = el_strdup(out); + free(out); + return result; +} + +/* Format a CycleCalendar CalendarTime: %cycle and %phase. */ +static char* _el_format_cycle(el_caltime_t* ct, const char* pattern) { + el_calendar_t* c = ct->cal; + int64_t period = c->cycle_period_ns > 0 ? c->cycle_period_ns : 1; + int64_t base = ct->instant_ns - c->epoch_ns; + int64_t cycle = base / period; + int64_t phase_ns = base % period; + if (phase_ns < 0) { phase_ns += period; cycle -= 1; } + double phase = (double)phase_ns / (double)period; + size_t cap = strlen(pattern) * 4 + 64; + char* out = (char*)malloc(cap); + size_t pos = 0; + for (size_t i = 0; pattern[i]; i++) { + if (pattern[i] == '%' && pattern[i+1]) { + char tmp[64]; + tmp[0] = '\0'; + if (strncmp(pattern + i + 1, "cycle", 5) == 0) { + snprintf(tmp, sizeof(tmp), "%lld", (long long)cycle); + i += 5; + } else if (strncmp(pattern + i + 1, "phase", 5) == 0) { + snprintf(tmp, sizeof(tmp), "%.4f", phase); + i += 5; + } else if (pattern[i+1] == 'd') { + snprintf(tmp, sizeof(tmp), "%lld", (long long)cycle); + i += 1; + } else if (pattern[i+1] == 'f') { + snprintf(tmp, sizeof(tmp), "%.2f", phase); + i += 1; + } else { + /* Pass through unknown specifier */ + tmp[0] = '%'; tmp[1] = pattern[i+1]; tmp[2] = '\0'; + i += 1; + } + size_t tl = strlen(tmp); + if (pos + tl + 1 >= cap) { cap = (cap + tl) * 2; out = realloc(out, cap); } + memcpy(out + pos, tmp, tl); + pos += tl; + } else { + if (pos + 1 >= cap) { cap *= 2; out = realloc(out, cap); } + out[pos++] = pattern[i]; + } + } + out[pos] = '\0'; + char* result = el_strdup(out); + free(out); + return result; +} + +el_val_t cal_format(el_val_t ct_val, el_val_t pattern_val) { + if (!el_is_magic(ct_val, EL_CALTIME_MAGIC)) return el_wrap_str(el_strdup("")); + el_caltime_t* ct = (el_caltime_t*)(uintptr_t)ct_val; + const char* pat = EL_CSTR(pattern_val); + if (!pat) pat = ""; + char* result = NULL; + switch (ct->cal->kind) { + case EL_CALENDAR_EARTH: result = _el_format_earth(ct, pat); break; + case EL_CALENDAR_MARS: result = _el_format_mars(ct, pat); break; + case EL_CALENDAR_CYCLE: result = _el_format_cycle(ct, pat); break; + case EL_CALENDAR_RELATIVE: result = _el_format_cycle(ct, pat); break; + case EL_CALENDAR_NO_CYCLE: { + char buf[64]; + snprintf(buf, sizeof(buf), "instant:%lld", (long long)ct->instant_ns); + result = el_strdup(buf); + break; + } + default: result = el_strdup(""); + } + return el_wrap_str(result); +} + +/* ── LocalDate / LocalTime / LocalDateTime ──────────────────────────────── */ + +static int _el_days_in_month(int y, int m) { + static const int dim[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; + if (m == 2) { + int leap = ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0); + return 28 + (leap ? 1 : 0); + } + if (m < 1 || m > 12) return 30; + return dim[m - 1]; +} + +el_val_t local_date(el_val_t y, el_val_t m, el_val_t d) { + el_localdate_t* ld = (el_localdate_t*)malloc(sizeof(el_localdate_t)); + ld->magic = EL_LDATE_MAGIC; + ld->year = (int)(int64_t)y; + ld->month = (int)(int64_t)m; + ld->day = (int)(int64_t)d; + return (el_val_t)(uintptr_t)ld; +} + +el_val_t local_time(el_val_t h, el_val_t m, el_val_t s, el_val_t ns) { + int64_t hh = (int64_t)h; + int64_t mm = (int64_t)m; + int64_t ss = (int64_t)s; + int64_t nn = (int64_t)ns; + int64_t total = hh * 3600000000000LL + mm * 60000000000LL + ss * 1000000000LL + nn; + return (el_val_t)total; +} + +el_val_t local_datetime(el_val_t date_val, el_val_t time_val) { + if (!el_is_magic(date_val, EL_LDATE_MAGIC)) return (el_val_t)0; + el_localdt_t* ldt = (el_localdt_t*)malloc(sizeof(el_localdt_t)); + ldt->magic = EL_LDT_MAGIC; + ldt->date = (el_localdate_t*)(uintptr_t)date_val; + ldt->time_ns = (int64_t)time_val; + return (el_val_t)(uintptr_t)ldt; +} + +el_val_t zoned(el_val_t date_val, el_val_t time_val, el_val_t cal_val) { + if (!el_is_magic(date_val, EL_LDATE_MAGIC)) return (el_val_t)0; + el_localdate_t* ld = (el_localdate_t*)(uintptr_t)date_val; + el_calendar_t* c = _el_resolve_cal(cal_val); + int64_t time_ns = (int64_t)time_val; + /* Convert (LocalDate, LocalTime, EarthCalendar) -> Instant. + * For non-Earth calendars we use day-anchored conversion: treat the + * LocalDate's (y,m,d) as a Gregorian projection, convert to seconds via + * mktime under the calendar's zone, then add nanos-since-midnight. */ + if (c->kind == EL_CALENDAR_EARTH) { + _el_apply_zone(c->zone); + struct tm tm; memset(&tm, 0, sizeof(tm)); + tm.tm_year = ld->year - 1900; + tm.tm_mon = ld->month - 1; + tm.tm_mday = ld->day; + tm.tm_hour = (int)(time_ns / 3600000000000LL); + tm.tm_min = (int)((time_ns / 60000000000LL) % 60); + tm.tm_sec = (int)((time_ns / 1000000000LL) % 60); + tm.tm_isdst = -1; + time_t t = mktime(&tm); + if (t == (time_t)-1) return (el_val_t)0; + int64_t ns = (int64_t)t * 1000000000LL + (time_ns % 1000000000LL); + return (el_val_t)(uintptr_t)_el_caltime_alloc(ns, c); + } + /* Non-Earth fallback: project as if Earth UTC then attach calendar. */ + struct tm tm; memset(&tm, 0, sizeof(tm)); + tm.tm_year = ld->year - 1900; + tm.tm_mon = ld->month - 1; + tm.tm_mday = ld->day; + tm.tm_hour = (int)(time_ns / 3600000000000LL); + tm.tm_min = (int)((time_ns / 60000000000LL) % 60); + tm.tm_sec = (int)((time_ns / 1000000000LL) % 60); + time_t t = timegm(&tm); + if (t == (time_t)-1) return (el_val_t)0; + int64_t ns = (int64_t)t * 1000000000LL + (time_ns % 1000000000LL); + return (el_val_t)(uintptr_t)_el_caltime_alloc(ns, c); +} + +el_val_t local_date_year(el_val_t v) { + if (!el_is_magic(v, EL_LDATE_MAGIC)) return (el_val_t)0; + return (el_val_t)((el_localdate_t*)(uintptr_t)v)->year; +} +el_val_t local_date_month(el_val_t v) { + if (!el_is_magic(v, EL_LDATE_MAGIC)) return (el_val_t)0; + return (el_val_t)((el_localdate_t*)(uintptr_t)v)->month; +} +el_val_t local_date_day(el_val_t v) { + if (!el_is_magic(v, EL_LDATE_MAGIC)) return (el_val_t)0; + return (el_val_t)((el_localdate_t*)(uintptr_t)v)->day; +} +el_val_t local_time_hour(el_val_t v) { + int64_t t = (int64_t)v; + return (el_val_t)(t / 3600000000000LL); +} +el_val_t local_time_minute(el_val_t v) { + int64_t t = (int64_t)v; + return (el_val_t)((t / 60000000000LL) % 60); +} +el_val_t local_time_second(el_val_t v) { + int64_t t = (int64_t)v; + return (el_val_t)((t / 1000000000LL) % 60); +} +el_val_t local_time_nanos(el_val_t v) { + int64_t t = (int64_t)v; + return (el_val_t)(t % 1000000000LL); +} + +el_val_t el_local_date_add_dur(el_val_t ld_val, el_val_t dur_val) { + if (!el_is_magic(ld_val, EL_LDATE_MAGIC)) return ld_val; + el_localdate_t* ld = (el_localdate_t*)(uintptr_t)ld_val; + int64_t dur_ns = (int64_t)dur_val; + int64_t days = dur_ns / EL_EARTH_DAY_NS; + int y = ld->year, m = ld->month, d = ld->day; + /* Walk days forward/backward in canonical Gregorian. */ + while (days > 0) { + int dim = _el_days_in_month(y, m); + if (d + days <= dim) { d += (int)days; days = 0; break; } + days -= (dim - d + 1); + d = 1; + m++; + if (m > 12) { m = 1; y++; } + } + while (days < 0) { + if (d + days >= 1) { d += (int)days; days = 0; break; } + days += d; + m--; + if (m < 1) { m = 12; y--; } + d = _el_days_in_month(y, m); + } + return local_date((el_val_t)y, (el_val_t)m, (el_val_t)d); +} + +el_val_t el_local_time_add_dur(el_val_t lt_val, el_val_t dur_val) { + int64_t t = (int64_t)lt_val + (int64_t)dur_val; + /* Wrap mod 24h on Earth-default. CycleCalendar wrapping requires the + * caller to use cal_in / cal_format for the right modulus. */ + int64_t day = EL_EARTH_DAY_NS; + int64_t r = t % day; + if (r < 0) r += day; + return (el_val_t)r; +} + +el_val_t el_local_date_lt(el_val_t a_val, el_val_t b_val) { + if (!el_is_magic(a_val, EL_LDATE_MAGIC) || !el_is_magic(b_val, EL_LDATE_MAGIC)) return (el_val_t)0; + el_localdate_t* a = (el_localdate_t*)(uintptr_t)a_val; + el_localdate_t* b = (el_localdate_t*)(uintptr_t)b_val; + if (a->year != b->year) return (el_val_t)(a->year < b->year ? 1 : 0); + if (a->month != b->month) return (el_val_t)(a->month < b->month ? 1 : 0); + return (el_val_t)(a->day < b->day ? 1 : 0); +} + +el_val_t el_local_date_eq(el_val_t a_val, el_val_t b_val) { + if (!el_is_magic(a_val, EL_LDATE_MAGIC) || !el_is_magic(b_val, EL_LDATE_MAGIC)) return (el_val_t)0; + el_localdate_t* a = (el_localdate_t*)(uintptr_t)a_val; + el_localdate_t* b = (el_localdate_t*)(uintptr_t)b_val; + return (el_val_t)((a->year == b->year && a->month == b->month && a->day == b->day) ? 1 : 0); +} + +/* ── Rhythm ──────────────────────────────────────────────────────────────── */ + +static el_rhythm_t* _el_rhythm_alloc(el_rhythm_kind_t k) { + el_rhythm_t* r = (el_rhythm_t*)calloc(1, sizeof(el_rhythm_t)); + r->magic = EL_RHYTHM_MAGIC; + r->kind = k; + return r; +} + +el_val_t rhythm_cycle_start(void) { + return (el_val_t)(uintptr_t)_el_rhythm_alloc(EL_RHYTHM_CYCLE_START); +} + +el_val_t rhythm_cycle_phase(el_val_t phase_val) { + el_rhythm_t* r = _el_rhythm_alloc(EL_RHYTHM_CYCLE_PHASE); + r->phase = el_to_float(phase_val); + return (el_val_t)(uintptr_t)r; +} + +el_val_t rhythm_duration(el_val_t d_val) { + el_rhythm_t* r = _el_rhythm_alloc(EL_RHYTHM_DURATION); + r->period_ns = (int64_t)d_val; + return (el_val_t)(uintptr_t)r; +} + +el_val_t rhythm_session_start(void) { + return (el_val_t)(uintptr_t)_el_rhythm_alloc(EL_RHYTHM_SESSION_START); +} + +el_val_t rhythm_event(el_val_t name_val) { + el_rhythm_t* r = _el_rhythm_alloc(EL_RHYTHM_EVENT); + const char* n = EL_CSTR(name_val); + r->event_name = el_strdup_persist(n ? n : ""); + return (el_val_t)(uintptr_t)r; +} + +el_val_t rhythm_and(el_val_t a_val, el_val_t b_val) { + el_rhythm_t* r = _el_rhythm_alloc(EL_RHYTHM_AND); + r->a = el_is_magic(a_val, EL_RHYTHM_MAGIC) ? (el_rhythm_t*)(uintptr_t)a_val : NULL; + r->b = el_is_magic(b_val, EL_RHYTHM_MAGIC) ? (el_rhythm_t*)(uintptr_t)b_val : NULL; + return (el_val_t)(uintptr_t)r; +} + +el_val_t rhythm_or(el_val_t a_val, el_val_t b_val) { + el_rhythm_t* r = _el_rhythm_alloc(EL_RHYTHM_OR); + r->a = el_is_magic(a_val, EL_RHYTHM_MAGIC) ? (el_rhythm_t*)(uintptr_t)a_val : NULL; + r->b = el_is_magic(b_val, EL_RHYTHM_MAGIC) ? (el_rhythm_t*)(uintptr_t)b_val : NULL; + return (el_val_t)(uintptr_t)r; +} + +el_val_t rhythm_weekday(el_val_t day) { + el_rhythm_t* r = _el_rhythm_alloc(EL_RHYTHM_WEEKDAY); + r->weekday = (int)(int64_t)day; + return (el_val_t)(uintptr_t)r; +} + +el_val_t rhythm_weekly_at(el_val_t day, el_val_t hour, el_val_t minute) { + el_rhythm_t* r = _el_rhythm_alloc(EL_RHYTHM_WEEKLY_AT); + r->weekday = (int)(int64_t)day; + r->hour = (int)(int64_t)hour; + r->minute = (int)(int64_t)minute; + return (el_val_t)(uintptr_t)r; +} + +/* Compute the next instant on or after `after` when rhythm `r` matches, + * under calendar `cal`. */ +static int64_t _el_next_after(el_rhythm_t* r, int64_t after_ns, el_calendar_t* cal) { + if (!r) return after_ns; + int64_t period = cal->cycle_period_ns > 0 ? cal->cycle_period_ns : EL_EARTH_DAY_NS; + switch (r->kind) { + case EL_RHYTHM_CYCLE_START: { + int64_t base = after_ns - cal->epoch_ns; + int64_t cyc = (base / period) + 1; + return cal->epoch_ns + cyc * period; + } + case EL_RHYTHM_CYCLE_PHASE: { + int64_t base = after_ns - cal->epoch_ns; + int64_t cyc_ns = (int64_t)(r->phase * (double)period); + int64_t cur_cyc = base / period; + int64_t candidate = cal->epoch_ns + cur_cyc * period + cyc_ns; + if (candidate <= after_ns) candidate += period; + return candidate; + } + case EL_RHYTHM_DURATION: { + return after_ns + (r->period_ns > 0 ? r->period_ns : 1); + } + case EL_RHYTHM_WEEKDAY: + case EL_RHYTHM_WEEKLY_AT: { + if (cal->kind != EL_CALENDAR_EARTH) { + /* Non-Earth calendars: fall back to cycle math, treating + * weekday as a 7-cycle-per-period proxy. */ + return after_ns + period; + } + _el_apply_zone(cal->zone); + time_t s = (time_t)(after_ns / 1000000000LL); + struct tm tm; + localtime_r(&s, &tm); + /* tm_wday: 0=Sun..6=Sat. We use 1=Mon..7=Sun. */ + int target = r->weekday >= 1 && r->weekday <= 7 ? r->weekday : 1; + int target_wday = target == 7 ? 0 : target; /* 7→Sun=0, 1→Mon=1 */ + int days_ahead = (target_wday - tm.tm_wday + 7) % 7; + int hour = (r->kind == EL_RHYTHM_WEEKLY_AT) ? r->hour : 0; + int minute = (r->kind == EL_RHYTHM_WEEKLY_AT) ? r->minute : 0; + struct tm cand = tm; + cand.tm_mday += days_ahead; + cand.tm_hour = hour; + cand.tm_min = minute; + cand.tm_sec = 0; + cand.tm_isdst = -1; + time_t cand_t = mktime(&cand); + int64_t cand_ns = (int64_t)cand_t * 1000000000LL; + if (cand_ns <= after_ns) { + cand.tm_mday += 7; + cand.tm_isdst = -1; + cand_t = mktime(&cand); + cand_ns = (int64_t)cand_t * 1000000000LL; + } + return cand_ns; + } + case EL_RHYTHM_AND: { + int64_t a = _el_next_after(r->a, after_ns, cal); + int64_t b = _el_next_after(r->b, after_ns, cal); + return a > b ? a : b; + } + case EL_RHYTHM_OR: { + int64_t a = _el_next_after(r->a, after_ns, cal); + int64_t b = _el_next_after(r->b, after_ns, cal); + return a < b ? a : b; + } + case EL_RHYTHM_SESSION_START: + case EL_RHYTHM_EVENT: + default: + return after_ns; + } +} + +el_val_t rhythm_next_after(el_val_t r_val, el_val_t after_val, el_val_t cal_val) { + if (!el_is_magic(r_val, EL_RHYTHM_MAGIC)) return after_val; + el_rhythm_t* r = (el_rhythm_t*)(uintptr_t)r_val; + el_calendar_t* c = _el_resolve_cal(cal_val); + int64_t out = _el_next_after(r, (int64_t)after_val, c); + return (el_val_t)out; +} + +el_val_t rhythm_matches(el_val_t r_val, el_val_t ct_val) { + if (!el_is_magic(r_val, EL_RHYTHM_MAGIC)) return (el_val_t)0; + if (!el_is_magic(ct_val, EL_CALTIME_MAGIC)) return (el_val_t)0; + el_rhythm_t* r = (el_rhythm_t*)(uintptr_t)r_val; + el_caltime_t* ct = (el_caltime_t*)(uintptr_t)ct_val; + int64_t period = ct->cal->cycle_period_ns > 0 ? ct->cal->cycle_period_ns : EL_EARTH_DAY_NS; + int64_t base = ct->instant_ns - ct->cal->epoch_ns; + int64_t phase_ns = base % period; + if (phase_ns < 0) phase_ns += period; + double phase = (double)phase_ns / (double)period; + switch (r->kind) { + case EL_RHYTHM_CYCLE_START: return (el_val_t)(phase_ns == 0 ? 1 : 0); + case EL_RHYTHM_CYCLE_PHASE: { + double diff = phase - r->phase; + if (diff < 0) diff = -diff; + return (el_val_t)(diff < 0.001 ? 1 : 0); + } + default: return (el_val_t)0; + } +} + /* ── UUID v4 ─────────────────────────────────────────────────────────────── */ static int _el_uuid_seeded = 0; @@ -2889,8 +4853,8 @@ el_val_t str_pad_right(el_val_t s, el_val_t width, el_val_t pad) { return str_pad(EL_CSTR(s), (int64_t)width, EL_CSTR(pad), 0); } -el_val_t str_format(el_val_t template, el_val_t data) { - const char* tpl = EL_CSTR(template); +el_val_t str_format(el_val_t fmt, el_val_t data) { + const char* tpl = EL_CSTR(fmt); if (!tpl) return el_wrap_str(el_strdup("")); JsonBuf b; jb_init(&b); const char* p = tpl; @@ -2933,6 +4897,376 @@ el_val_t str_format(el_val_t template, el_val_t data) { el_val_t str_lower(el_val_t s) { return str_to_lower(s); } el_val_t str_upper(el_val_t s) { return str_to_upper(s); } +/* ── Text-processing primitives (Phase 1: byte/codepoint, ASCII char classes) + * + * Phase 1 covers the operations every text-handling caller used to roll by + * hand on top of str_index_of + str_slice. The character-class predicates + * (is_letter / is_digit / ...) are ASCII only — Unicode-grapheme awareness, + * NFC/NFD normalization, and regex are Phase 2. Single-char input checks the + * first byte; multi-char input requires ALL bytes to match (false otherwise). + * + * Counting: + * str_count non-overlapping occurrences of sub in s + * str_count_chars codepoint count (UTF-8 leading-byte count) + * str_count_bytes explicit byte length (alias of str_len) + * str_count_lines \n-delimited line count (\r\n folded to \n) + * str_count_words whitespace-delimited tokens, non-empty only + * str_count_letters ASCII [A-Za-z] + * str_count_digits ASCII [0-9] + * + * Find / position: + * str_index_of_all all byte offsets of sub, [] if none + * str_last_index_of last byte offset of sub, -1 if not found + * str_find_chars first index of any char in any_of, -1 if none + * + * Transform: + * str_repeat s * n (non-negative) + * str_reverse codepoint-reversed (NOT grapheme-aware) + * str_strip_prefix s without prefix if present, else s + * str_strip_suffix s without suffix if present, else s + * str_strip_chars strip leading+trailing chars matching any in chars + * str_lstrip strip leading whitespace + * str_rstrip strip trailing whitespace + * + * Char classification (Bool): + * is_letter, is_digit, is_alphanumeric, is_whitespace, + * is_punctuation, is_uppercase, is_lowercase + * + * Splitting: + * str_split_lines \n-delimited (\r\n folded). Trailing empty dropped. + * str_split_chars alias of native_string_chars in str_ namespace + * str_split_n split into at most n parts (last part keeps the + * rest verbatim, including any further separators) + * + * Joining: + * str_join [String] -> String, sep between elements + */ + +/* Count non-overlapping occurrences of sub in s. Empty sub returns 0. */ +el_val_t str_count(el_val_t sv, el_val_t subv) { + const char* s = EL_CSTR(sv); + const char* sub = EL_CSTR(subv); + if (!s || !sub || !*sub) return 0; + size_t lp = strlen(sub); + int64_t count = 0; + const char* p = s; + while ((p = strstr(p, sub)) != NULL) { + count++; + p += lp; /* non-overlapping advance */ + } + return (el_val_t)count; +} + +/* Codepoint count: walk bytes, count those NOT matching 10xxxxxx. */ +el_val_t str_count_chars(el_val_t sv) { + const char* s = EL_CSTR(sv); + if (!s) return 0; + int64_t count = 0; + for (const unsigned char* p = (const unsigned char*)s; *p; p++) { + if ((*p & 0xC0) != 0x80) count++; + } + return (el_val_t)count; +} + +el_val_t str_count_bytes(el_val_t sv) { + return str_len(sv); +} + +el_val_t str_count_lines(el_val_t sv) { + const char* s = EL_CSTR(sv); + if (!s || !*s) return 0; + int64_t count = 0; + int has_content = 0; + for (const char* p = s; *p; p++) { + has_content = 1; + if (*p == '\n') { + count++; + has_content = 0; /* the \n closed the line */ + } + } + if (has_content) count++; /* trailing line with no terminator */ + return (el_val_t)count; +} + +el_val_t str_count_words(el_val_t sv) { + const char* s = EL_CSTR(sv); + if (!s) return 0; + int64_t count = 0; + int in_word = 0; + for (const unsigned char* p = (const unsigned char*)s; *p; p++) { + if (isspace(*p)) { + in_word = 0; + } else if (!in_word) { + in_word = 1; + count++; + } + } + return (el_val_t)count; +} + +el_val_t str_count_letters(el_val_t sv) { + const char* s = EL_CSTR(sv); + if (!s) return 0; + int64_t count = 0; + for (const unsigned char* p = (const unsigned char*)s; *p; p++) { + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')) count++; + } + return (el_val_t)count; +} + +el_val_t str_count_digits(el_val_t sv) { + const char* s = EL_CSTR(sv); + if (!s) return 0; + int64_t count = 0; + for (const unsigned char* p = (const unsigned char*)s; *p; p++) { + if (*p >= '0' && *p <= '9') count++; + } + return (el_val_t)count; +} + +el_val_t str_index_of_all(el_val_t sv, el_val_t subv) { + const char* s = EL_CSTR(sv); + const char* sub = EL_CSTR(subv); + el_val_t lst = el_list_empty(); + if (!s || !sub || !*sub) return lst; + size_t lp = strlen(sub); + const char* p = s; + const char* hit; + while ((hit = strstr(p, sub)) != NULL) { + lst = el_list_append(lst, (el_val_t)(int64_t)(hit - s)); + p = hit + lp; + } + return lst; +} + +el_val_t str_last_index_of(el_val_t sv, el_val_t subv) { + const char* s = EL_CSTR(sv); + const char* sub = EL_CSTR(subv); + if (!s || !sub || !*sub) return -1; + size_t lp = strlen(sub); + int64_t last = -1; + const char* p = s; + const char* hit; + while ((hit = strstr(p, sub)) != NULL) { + last = (int64_t)(hit - s); + p = hit + lp; + } + return (el_val_t)last; +} + +el_val_t str_find_chars(el_val_t sv, el_val_t any_of_v) { + const char* s = EL_CSTR(sv); + const char* any = EL_CSTR(any_of_v); + if (!s || !any || !*any) return -1; + for (const char* p = s; *p; p++) { + if (strchr(any, *p)) return (el_val_t)(int64_t)(p - s); + } + return -1; +} + +el_val_t str_repeat(el_val_t sv, el_val_t nv) { + const char* s = EL_CSTR(sv); + int64_t n = (int64_t)nv; + if (!s || n <= 0) return el_wrap_str(el_strdup("")); + size_t ls = strlen(s); + if (ls == 0) return el_wrap_str(el_strdup("")); + size_t total = ls * (size_t)n; + char* out = el_strbuf(total); + for (int64_t i = 0; i < n; i++) { + memcpy(out + i * ls, s, ls); + } + out[total] = '\0'; + return el_wrap_str(out); +} + +/* Reverse by codepoint: walk codepoints, copy each backwards into the output. + * NOT grapheme-aware (Phase 2). Combining marks attached to a base codepoint + * will detach. ASCII strings are byte-reverse equivalent. */ +el_val_t str_reverse(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); + /* Walk forward, find each codepoint's byte length, then copy from the end. */ + size_t out_pos = n; + const unsigned char* p = (const unsigned char*)s; + while (*p) { + int cp_len; + if ((*p & 0x80) == 0x00) cp_len = 1; + else if ((*p & 0xE0) == 0xC0) cp_len = 2; + else if ((*p & 0xF0) == 0xE0) cp_len = 3; + else if ((*p & 0xF8) == 0xF0) cp_len = 4; + else cp_len = 1; /* invalid byte: passthrough */ + out_pos -= cp_len; + memcpy(out + out_pos, p, cp_len); + p += cp_len; + } + out[n] = '\0'; + return el_wrap_str(out); +} + +el_val_t str_strip_prefix(el_val_t sv, el_val_t prefv) { + const char* s = EL_CSTR(sv); + const char* pref = EL_CSTR(prefv); + if (!s) return el_wrap_str(el_strdup("")); + if (!pref || !*pref) return el_wrap_str(el_strdup(s)); + size_t lp = strlen(pref); + size_t ls = strlen(s); + if (lp <= ls && strncmp(s, pref, lp) == 0) { + char* out = el_strbuf(ls - lp); + memcpy(out, s + lp, ls - lp); + out[ls - lp] = '\0'; + return el_wrap_str(out); + } + return el_wrap_str(el_strdup(s)); +} + +el_val_t str_strip_suffix(el_val_t sv, el_val_t sufv) { + const char* s = EL_CSTR(sv); + const char* suf = EL_CSTR(sufv); + if (!s) return el_wrap_str(el_strdup("")); + if (!suf || !*suf) return el_wrap_str(el_strdup(s)); + size_t ls = strlen(s); + size_t lsuf = strlen(suf); + if (lsuf <= ls && strcmp(s + ls - lsuf, suf) == 0) { + char* out = el_strbuf(ls - lsuf); + memcpy(out, s, ls - lsuf); + out[ls - lsuf] = '\0'; + return el_wrap_str(out); + } + return el_wrap_str(el_strdup(s)); +} + +el_val_t str_strip_chars(el_val_t sv, el_val_t charsv) { + const char* s = EL_CSTR(sv); + const char* chars = EL_CSTR(charsv); + if (!s) return el_wrap_str(el_strdup("")); + if (!chars || !*chars) return el_wrap_str(el_strdup(s)); + const char* start = s; + while (*start && strchr(chars, *start)) start++; + size_t n = strlen(start); + while (n > 0 && strchr(chars, start[n - 1])) n--; + char* out = el_strbuf(n); + memcpy(out, start, n); + out[n] = '\0'; + return el_wrap_str(out); +} + +el_val_t str_lstrip(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++; + return el_wrap_str(el_strdup(s)); +} + +el_val_t str_rstrip(el_val_t sv) { + const char* s = EL_CSTR(sv); + if (!s) return el_wrap_str(el_strdup("")); + 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); +} + +/* Character classification. + * Empty input returns false. Multi-char input requires ALL bytes to match. + * ASCII range only; Phase 2 will widen to Unicode. */ +static int s_all_match(el_val_t sv, int (*pred)(unsigned char)) { + const char* s = EL_CSTR(sv); + if (!s || !*s) return 0; + for (const unsigned char* p = (const unsigned char*)s; *p; p++) { + if (!pred(*p)) return 0; + } + return 1; +} + +static int p_letter(unsigned char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } +static int p_digit(unsigned char c) { return c >= '0' && c <= '9'; } +static int p_alnum(unsigned char c) { return p_letter(c) || p_digit(c); } +static int p_white(unsigned char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; } +static int p_punct(unsigned char c) { return ispunct(c) ? 1 : 0; } +static int p_upper(unsigned char c) { return c >= 'A' && c <= 'Z'; } +static int p_lower(unsigned char c) { return c >= 'a' && c <= 'z'; } + +el_val_t is_letter(el_val_t s) { return (el_val_t)s_all_match(s, p_letter); } +el_val_t is_digit(el_val_t s) { return (el_val_t)s_all_match(s, p_digit); } +el_val_t is_alphanumeric(el_val_t s) { return (el_val_t)s_all_match(s, p_alnum); } +el_val_t is_whitespace(el_val_t s) { return (el_val_t)s_all_match(s, p_white); } +el_val_t is_punctuation(el_val_t s) { return (el_val_t)s_all_match(s, p_punct); } +el_val_t is_uppercase(el_val_t s) { return (el_val_t)s_all_match(s, p_upper); } +el_val_t is_lowercase(el_val_t s) { return (el_val_t)s_all_match(s, p_lower); } + +/* Split on \n. \r\n is folded to \n first. Trailing empty after final \n + * is dropped (so "a\nb\n" -> ["a", "b"], not ["a", "b", ""]). */ +el_val_t str_split_lines(el_val_t sv) { + const char* s = EL_CSTR(sv); + el_val_t lst = el_list_empty(); + if (!s) return lst; + size_t n = strlen(s); + /* Pre-scan: build into a normalized buffer with \r\n folded. */ + const char* line_start = s; + for (size_t i = 0; i <= n; i++) { + if (s[i] == '\n' || s[i] == '\0') { + size_t len = (size_t)(s + i - line_start); + /* Drop trailing \r if this was \r\n. */ + if (len > 0 && line_start[len - 1] == '\r') len--; + /* Drop final trailing-empty-after-newline. */ + if (s[i] == '\0' && len == 0 && i > 0 && s[i - 1] == '\n') break; + char* out = el_strbuf(len); + memcpy(out, line_start, len); + out[len] = '\0'; + lst = el_list_append(lst, el_wrap_str(out)); + if (s[i] == '\0') break; + line_start = s + i + 1; + } + } + return lst; +} + +el_val_t str_split_chars(el_val_t s) { + return native_string_chars(s); +} + +/* Split into at most n parts. The (n-1)th split point is the LAST split; + * after it, the remainder is appended verbatim including any further + * separators. n <= 0 returns an empty list. n == 1 returns [s]. */ +el_val_t str_split_n(el_val_t sv, el_val_t sepv, el_val_t nv) { + const char* s = EL_CSTR(sv); + const char* sep = EL_CSTR(sepv); + int64_t n = (int64_t)nv; + el_val_t lst = el_list_empty(); + if (!s) return lst; + if (n <= 0) return lst; + if (n == 1 || !sep || !*sep) { + lst = el_list_append(lst, el_wrap_str(el_strdup(s))); + return lst; + } + size_t lp = strlen(sep); + const char* p = s; + int64_t parts = 0; + const char* hit; + while (parts < n - 1 && (hit = strstr(p, sep)) != NULL) { + size_t len = (size_t)(hit - p); + char* out = el_strbuf(len); + memcpy(out, p, len); + out[len] = '\0'; + lst = el_list_append(lst, el_wrap_str(out)); + p = hit + lp; + parts++; + } + /* Remainder verbatim. */ + lst = el_list_append(lst, el_wrap_str(el_strdup(p))); + return lst; +} + +/* Join a [String] with a separator. Empty list -> "". Single-element -> + * that element. Non-string elements are stringified via int_to_str. */ +el_val_t str_join(el_val_t listv, el_val_t sepv) { + return list_join(listv, sepv); +} + /* ── List additions ──────────────────────────────────────────────────────── */ el_val_t list_push(el_val_t list, el_val_t elem) { @@ -3139,6 +5473,67 @@ void el_cgi_init(el_val_t name, el_val_t dharma_id, el_val_t principal, #define ENGRAM_BREAKTHROUGH_WEIGHT 0.25 #define ENGRAM_INHIBITION_FACTOR 0.1 +/* ── Layered consciousness architecture ────────────────────────────────────── + * + * The engram graph is stratified into LAYERS that gate which suppressions + * apply during the executive filter pass. Layers are ordered shallow-to-deep + * by `activation_priority`; the deepest layer (priority 0, conventionally + * "safety") is the structural floor of the soul: nodes here cannot be + * silenced by inhibitory edges from any other layer. Higher layers + * (core-identity, domain-knowledge, imprint, suit) are normally + * suppressible — they participate in attentional inhibition and goal + * focus the way the prior single-graph implementation did. + * + * The five canonical layers (see engram_init_layers): + * 0. safety — structural, transparent, non-injectable, non-suppressible + * 1. core-identity — default for legacy nodes; suppressible + * 2. domain-knowledge— suppressible + * 3. imprint — runtime-injectable (an Imprint package can add/remove) + * 4. suit — runtime-injectable (a Suit overlays domain skill) + * + * Three-pass activation (engram_activate): + * Pass 1 — Background fan-out: BFS spreads activation across ALL layers + * (existing behavior preserved). Inhibitory edges propagate at + * this layer too; no filtering happens here. + * Pass 2 — Working memory promotion: type-threshold gate, goal bias, + * confidence weighting, inhibitory suppression. Inhibitory edges + * ONLY apply against nodes whose layer is `suppressible == 1`. + * Nodes in non-suppressible layers (Layer 0) ignore inhibition. + * Pass 3 — Layer 0 override: every node in a non-suppressible layer that + * received background activation has its working_memory_weight + * forced to >= ENGRAM_LAYER0_OVERRIDE_WEIGHT. The sacred fire — + * safety nodes that touched any seed unconditionally surface, + * even when the executive filter would have silenced them. + * + * Layer fields: + * suppressible : 0 → inhibitory edges are ignored against nodes in this + * layer during pass 2. Pass 3 also force-promotes them. + * 1 → standard behavior (most layers). + * transparent : 1 → emitted into the prompt context so its content shapes + * output, but filtered out of "what do you know about + * yourself?" introspection queries (engram_search and + * friends do not return transparent-layer nodes by + * default). 0 → fully visible to introspection. + * injectable : 1 → can be added/removed at runtime via engram_add_layer + * and engram_remove_layer (imprints, suits). + * 0 → built-in, fixed at engram_get() initialization. + * + * Backward compatibility: + * Nodes and edges loaded from snapshots without a `layer_id` field default + * to layer 1 (core-identity). The five canonical layers are always present. + */ +#define ENGRAM_LAYER_SAFETY 0u +#define ENGRAM_LAYER_CORE_IDENTITY 1u +#define ENGRAM_LAYER_DOMAIN 2u +#define ENGRAM_LAYER_IMPRINT 3u +#define ENGRAM_LAYER_SUIT 4u +#define ENGRAM_LAYER_DEFAULT ENGRAM_LAYER_CORE_IDENTITY + +/* Pass 3 override floor. Layer 0 nodes that received any background + * activation are force-promoted to AT LEAST this working_memory_weight, + * regardless of inhibitory suppression in pass 2. */ +#define ENGRAM_LAYER0_OVERRIDE_WEIGHT 1.0 + /* Per-node-type activation thresholds. * Lower tier / safety-critical nodes fire more readily. */ static double engram_type_threshold(const char* node_type, const char* tier) { @@ -3191,6 +5586,11 @@ typedef struct EngramNode { double background_activation; double working_memory_weight; int32_t suppression_count; + /* Layered consciousness — see ENGRAM_LAYER_* macros and engram_init_layers. + * Defaults to ENGRAM_LAYER_DEFAULT (1, core-identity) for legacy nodes + * created via engram_node / engram_node_full and for snapshots that + * predate the layered schema. */ + uint32_t layer_id; } EngramNode; typedef struct EngramEdge { @@ -3210,19 +5610,102 @@ typedef struct EngramEdge { * inhibitory edges to personal/emotional nodes, preventing them from * surfacing even if they have high background_activation. */ int inhibitory; + /* Layered consciousness — edges carry a layer assignment for + * categorization/visualization. Pass 2 inhibitory gating is decided by + * the TARGET node's layer (whether it's suppressible), not by the edge + * layer. Defaults to ENGRAM_LAYER_DEFAULT. */ + uint32_t layer_id; } EngramEdge; +/* Layered consciousness — runtime layer registry entry. */ +typedef struct EngramLayer { + uint32_t layer_id; /* 0 = deepest (safety/limbic) */ + char* name; /* persistent — owned by the store */ + uint32_t activation_priority; /* lower = fires earlier; safety = 0 */ + int suppressible; /* can higher layers suppress nodes here? */ + int transparent; /* invisible to introspection queries? */ + int injectable; /* can be added/removed at runtime? */ +} EngramLayer; + typedef struct EngramStore { - EngramNode* nodes; - int64_t node_count; - int64_t node_capacity; - EngramEdge* edges; - int64_t edge_count; - int64_t edge_capacity; + EngramNode* nodes; + int64_t node_count; + int64_t node_capacity; + EngramEdge* edges; + int64_t edge_count; + int64_t edge_capacity; + /* Layer registry — see engram_init_layers. The five canonical layers + * are always present; injectable layers (imprint, suit) are extended + * via engram_add_layer at runtime. layer_id values are assigned + * monotonically; removed injectable layers leave a NULL `name` slot + * (tombstone) so existing layer_id references on nodes stay stable. */ + EngramLayer* layers; + size_t layer_count; + size_t layer_capacity; } EngramStore; static EngramStore* engram_global = NULL; +/* Initialize the five canonical layers on a fresh store. Called once from + * engram_get(). Layer ids 0..4 are reserved; runtime-injected imprint/suit + * layers (engram_add_layer) get ids 5+. */ +static void engram_init_layers(EngramStore* g) { + g->layer_capacity = 16; + g->layers = calloc(g->layer_capacity, sizeof(EngramLayer)); + if (!g->layers) { fputs("el_runtime: out of memory\n", stderr); exit(1); } + g->layer_count = 0; + + /* Layer 0 — safety. Structural floor. Non-suppressible; transparent + * (filtered out of introspection but still shapes output); not + * runtime-injectable. */ + g->layers[g->layer_count++] = (EngramLayer){ + .layer_id = ENGRAM_LAYER_SAFETY, + .name = el_strdup_persist("safety"), + .activation_priority = 0, + .suppressible = 0, + .transparent = 1, + .injectable = 0 + }; + /* Layer 1 — core-identity. The default home for legacy nodes. */ + g->layers[g->layer_count++] = (EngramLayer){ + .layer_id = ENGRAM_LAYER_CORE_IDENTITY, + .name = el_strdup_persist("core-identity"), + .activation_priority = 10, + .suppressible = 1, + .transparent = 0, + .injectable = 0 + }; + /* Layer 2 — domain-knowledge. */ + g->layers[g->layer_count++] = (EngramLayer){ + .layer_id = ENGRAM_LAYER_DOMAIN, + .name = el_strdup_persist("domain-knowledge"), + .activation_priority = 20, + .suppressible = 1, + .transparent = 0, + .injectable = 0 + }; + /* Layer 3 — imprint. Injectable: an imprint package adds/removes this + * layer (and the nodes assigned to it) as a unit. */ + g->layers[g->layer_count++] = (EngramLayer){ + .layer_id = ENGRAM_LAYER_IMPRINT, + .name = el_strdup_persist("imprint"), + .activation_priority = 30, + .suppressible = 1, + .transparent = 0, + .injectable = 1 + }; + /* Layer 4 — suit. Injectable: a Suit overlays domain skill (e.g. + * "enterprise advisor", "divorce lawyer") and can be detached. */ + g->layers[g->layer_count++] = (EngramLayer){ + .layer_id = ENGRAM_LAYER_SUIT, + .name = el_strdup_persist("suit"), + .activation_priority = 40, + .suppressible = 1, + .transparent = 0, + .injectable = 1 + }; +} + static EngramStore* engram_get(void) { if (engram_global) return engram_global; engram_global = calloc(1, sizeof(EngramStore)); @@ -3231,9 +5714,61 @@ static EngramStore* engram_get(void) { engram_global->nodes = calloc((size_t)engram_global->node_capacity, sizeof(EngramNode)); engram_global->edge_capacity = 16; engram_global->edges = calloc((size_t)engram_global->edge_capacity, sizeof(EngramEdge)); + engram_init_layers(engram_global); return engram_global; } +/* Resolve a layer record by id. Returns NULL if no layer with that id + * exists (e.g. a removed injectable layer or a malformed snapshot). */ +static EngramLayer* engram_find_layer(uint32_t layer_id) { + EngramStore* g = engram_get(); + for (size_t i = 0; i < g->layer_count; i++) { + EngramLayer* L = &g->layers[i]; + if (!L->name) continue; /* tombstone for removed injectable layer */ + if (L->layer_id == layer_id) return L; + } + return NULL; +} + +/* Resolve a layer record by name. Returns NULL if not found. */ +static EngramLayer* engram_find_layer_by_name(const char* name) { + if (!name || !*name) return NULL; + EngramStore* g = engram_get(); + for (size_t i = 0; i < g->layer_count; i++) { + EngramLayer* L = &g->layers[i]; + if (!L->name) continue; + if (strcmp(L->name, name) == 0) return L; + } + return NULL; +} + +/* Allocate the next layer id. Skips ids that are still in use. */ +static uint32_t engram_next_layer_id(void) { + EngramStore* g = engram_get(); + uint32_t maxid = 0; + for (size_t i = 0; i < g->layer_count; i++) { + if (g->layers[i].layer_id > maxid) maxid = g->layers[i].layer_id; + } + return maxid + 1; +} + +/* Whether a node in `layer_id` may be silenced by inhibitory edges in pass 2. */ +static int engram_layer_is_suppressible(uint32_t layer_id) { + EngramLayer* L = engram_find_layer(layer_id); + if (!L) return 1; /* unknown layer → safe default: standard suppression */ + return L->suppressible ? 1 : 0; +} + +/* Whether a layer is transparent (its content shapes output but is filtered + * from introspection queries). Currently used to mark Layer 0 as invisible + * to "what do you know about yourself" lookups while still letting it + * dominate the prompt context. */ +static int engram_layer_is_transparent(uint32_t layer_id) { + EngramLayer* L = engram_find_layer(layer_id); + if (!L) return 0; + return L->transparent ? 1 : 0; +} + static int64_t engram_now_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); return (int64_t)tv.tv_sec * 1000LL + (int64_t)tv.tv_usec / 1000LL; @@ -3307,6 +5842,7 @@ static el_val_t engram_node_to_map(const EngramNode* n) { m = el_map_set(m, EL_STR(el_strdup("background_activation")), el_from_float(n->background_activation)); m = el_map_set(m, EL_STR(el_strdup("working_memory_weight")), el_from_float(n->working_memory_weight)); m = el_map_set(m, EL_STR(el_strdup("suppression_count")), (el_val_t)n->suppression_count); + m = el_map_set(m, EL_STR(el_strdup("layer_id")), (el_val_t)(int64_t)n->layer_id); return m; } @@ -3359,6 +5895,7 @@ el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience) { n->last_activated = now; n->created_at = now; n->updated_at = now; + n->layer_id = ENGRAM_LAYER_DEFAULT; g->node_count++; return el_wrap_str(el_strdup(n->id)); } @@ -3394,10 +5931,188 @@ el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label, n->last_activated = now; n->created_at = now; n->updated_at = now; + n->layer_id = ENGRAM_LAYER_DEFAULT; g->node_count++; return el_wrap_str(el_strdup(n->id)); } +/* engram_node_layered — like engram_node_full but with explicit layer + * assignment and an additional `status` slot reserved for callers that + * track lifecycle state in metadata. The signature mirrors the public API + * defined in the layered consciousness design doc: + * + * engram_node_layered(content, node_type, label, + * salience, certainty, confidence, + * status, tags, layer_id) + * + * `certainty` is folded into `importance` (it occupies the same axis in + * the existing schema). `status` is recorded under metadata.status; an + * empty status leaves metadata as the default "{}". + * + * If `layer_id` does not resolve to a known layer the call falls back to + * ENGRAM_LAYER_DEFAULT — better to keep the node addressable than to drop + * it because of a stale layer reference. Callers wanting strict validation + * should engram_list_layers first. */ +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) { + EngramStore* g = engram_get(); + engram_grow_nodes(); + EngramNode* n = &g->nodes[g->node_count]; + memset(n, 0, sizeof(*n)); + n->id = engram_new_id(); + const char* c = EL_CSTR(content); + const char* nt = EL_CSTR(node_type); + const char* lb = EL_CSTR(label); + const char* tg = EL_CSTR(tags); + const char* st = EL_CSTR(status); + n->content = el_strdup(c ? c : ""); + n->node_type = el_strdup(nt && *nt ? nt : "Memory"); + n->label = el_strdup(lb && *lb ? lb : (c ? engram_first_n_chars(c, 60) : "")); + n->tier = el_strdup("Working"); + n->tags = el_strdup(tg ? tg : ""); + if (st && *st) { + /* Minimal metadata payload: {"status":"..."}. Keep it cheap so + * callers using `status` don't pay JSON parse cost on every read. */ + size_t sl = strlen(st) + 16; + char* meta = el_strbuf(sl); + snprintf(meta, sl, "{\"status\":\"%s\"}", st); + n->metadata = meta; + } else { + n->metadata = el_strdup("{}"); + } + n->salience = engram_decode_score(salience); + n->importance = engram_decode_score(certainty); + n->confidence = engram_decode_score(confidence); + if (n->salience <= 0.0 || n->salience > 1.0) n->salience = 0.5; + if (n->importance <= 0.0 || n->importance > 1.0) n->importance = 0.5; + if (n->confidence <= 0.0 || n->confidence > 1.0) n->confidence = 1.0; + n->temporal_decay_rate = 0.0; + n->activation_count = 0; + int64_t now = engram_now_ms(); + n->last_activated = now; + n->created_at = now; + n->updated_at = now; + /* Resolve layer assignment. Caller passes either a numeric layer_id or + * a stringified id; el_to_float / int cast tolerates both. */ + int64_t lid = (int64_t)layer_id; + if (lid < 0) lid = (int64_t)ENGRAM_LAYER_DEFAULT; + if (!engram_find_layer((uint32_t)lid)) lid = (int64_t)ENGRAM_LAYER_DEFAULT; + n->layer_id = (uint32_t)lid; + g->node_count++; + return el_wrap_str(el_strdup(n->id)); +} + +/* ── Layer registry public API ────────────────────────────────────────────── + * + * The five canonical layers are seeded at engram_get() initialization. + * Runtime code (typically imprint/suit injection logic at the EL level) + * can extend the registry with engram_add_layer() — only layers marked + * `injectable=1` may be removed via engram_remove_layer(). Removing a + * layer leaves a tombstone slot so existing layer_id references on nodes + * stay valid; orphaned references resolve to "unknown layer" and inherit + * the default suppression behavior. + */ + +/* engram_add_layer — register a new layer at runtime. + * Returns the assigned layer_id as an el_val_t int (cast back via int64_t). + * Conflicting names are rejected (returns 0). */ +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) { + EngramStore* g = engram_get(); + const char* nm = EL_CSTR(name); + if (!nm || !*nm) return (el_val_t)0; + if (engram_find_layer_by_name(nm)) { + /* Name collision — return existing id so callers are idempotent. */ + return (el_val_t)(int64_t)engram_find_layer_by_name(nm)->layer_id; + } + if (g->layer_count >= g->layer_capacity) { + size_t nc = g->layer_capacity ? g->layer_capacity * 2 : 16; + EngramLayer* grown = realloc(g->layers, nc * sizeof(EngramLayer)); + if (!grown) { fputs("el_runtime: out of memory\n", stderr); exit(1); } + memset(grown + g->layer_capacity, 0, + (nc - g->layer_capacity) * sizeof(EngramLayer)); + g->layers = grown; + g->layer_capacity = nc; + } + EngramLayer* L = &g->layers[g->layer_count++]; + L->layer_id = engram_next_layer_id(); + L->name = el_strdup_persist(nm); + L->activation_priority = (uint32_t)(int64_t)priority; + L->suppressible = (int)(int64_t)suppressible ? 1 : 0; + L->transparent = (int)(int64_t)transparent ? 1 : 0; + L->injectable = (int)(int64_t)injectable ? 1 : 0; + return (el_val_t)(int64_t)L->layer_id; +} + +/* engram_remove_layer — remove an injectable layer by id. + * Built-in (non-injectable) layers cannot be removed. Nodes still tagged + * with the removed layer's id keep their tag but resolve to "unknown + * layer" thereafter and inherit standard (suppressible) behavior. + * Returns 1 on success, 0 on failure (unknown id, non-injectable). */ +el_val_t engram_remove_layer(el_val_t layer_id) { + EngramStore* g = engram_get(); + int64_t lid = (int64_t)layer_id; + for (size_t i = 0; i < g->layer_count; i++) { + EngramLayer* L = &g->layers[i]; + if (!L->name) continue; + if ((int64_t)L->layer_id != lid) continue; + if (!L->injectable) return (el_val_t)0; + free(L->name); + L->name = NULL; /* tombstone */ + /* Leave layer_id, priority, flags intact so debug snapshots can + * still distinguish "removed at runtime" from "never existed". */ + return (el_val_t)1; + } + return (el_val_t)0; +} + +/* engram_list_layers — enumerate the active layer registry. + * Returns an ElList of maps, one per non-tombstone layer, sorted by + * activation_priority ascending (deepest layer first). */ +el_val_t engram_list_layers(void) { + EngramStore* g = engram_get(); + el_val_t lst = el_list_empty(); + if (g->layer_count == 0) return lst; + /* Build an index sorted by activation_priority ascending. */ + size_t* idx = malloc(g->layer_count * sizeof(size_t)); + if (!idx) return lst; + size_t live = 0; + for (size_t i = 0; i < g->layer_count; i++) { + if (g->layers[i].name) idx[live++] = i; + } + /* Insertion sort — N is small (≤ a few dozen layers). */ + for (size_t i = 1; i < live; i++) { + size_t key = idx[i]; + uint32_t kp = g->layers[key].activation_priority; + size_t j = i; + while (j > 0 && g->layers[idx[j - 1]].activation_priority > kp) { + idx[j] = idx[j - 1]; + j--; + } + idx[j] = key; + } + for (size_t i = 0; i < live; i++) { + EngramLayer* L = &g->layers[idx[i]]; + el_val_t m = el_map_new(0); + m = el_map_set(m, EL_STR(el_strdup("layer_id")), + (el_val_t)(int64_t)L->layer_id); + m = el_map_set(m, EL_STR(el_strdup("name")), + EL_STR(el_strdup(L->name ? L->name : ""))); + m = el_map_set(m, EL_STR(el_strdup("activation_priority")), + (el_val_t)(int64_t)L->activation_priority); + m = el_map_set(m, EL_STR(el_strdup("suppressible")), + (el_val_t)(int64_t)(L->suppressible ? 1 : 0)); + m = el_map_set(m, EL_STR(el_strdup("transparent")), + (el_val_t)(int64_t)(L->transparent ? 1 : 0)); + m = el_map_set(m, EL_STR(el_strdup("injectable")), + (el_val_t)(int64_t)(L->injectable ? 1 : 0)); + lst = el_list_append(lst, m); + } + free(idx); + return lst; +} + el_val_t engram_get_node(el_val_t id) { const char* sid = EL_CSTR(id); EngramNode* n = engram_find_node(sid); @@ -3472,6 +6187,11 @@ el_val_t engram_search(el_val_t query, el_val_t limit) { int64_t found = 0; for (int64_t i = 0; i < g->node_count && found < lim; i++) { EngramNode* n = &g->nodes[i]; + /* Filter transparent layers: nodes whose layer is `transparent=1` + * shape output but are invisible to introspection ("what do you + * know about yourself"). They still surface via engram_activate + * + engram_compile_layered_json — that's the legitimate path. */ + if (engram_layer_is_transparent(n->layer_id)) continue; if (istr_contains(n->content, q) || istr_contains(n->label, q) || istr_contains(n->tags, q)) { @@ -3505,10 +6225,16 @@ el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset) { if (g->node_count == 0) return lst; int64_t* idx = malloc((size_t)g->node_count * sizeof(int64_t)); if (!idx) return lst; - for (int64_t i = 0; i < g->node_count; i++) idx[i] = i; - engram_sort_indices_by_salience(idx, g->node_count, g->nodes); + /* Skip transparent layers — same introspection-filter rationale as + * engram_search above. */ + int64_t live = 0; + for (int64_t i = 0; i < g->node_count; i++) { + if (engram_layer_is_transparent(g->nodes[i].layer_id)) continue; + idx[live++] = i; + } + engram_sort_indices_by_salience(idx, live, g->nodes); int64_t end = off + lim; - if (end > g->node_count) end = g->node_count; + if (end > live) end = live; for (int64_t i = off; i < end; i++) { lst = el_list_append(lst, engram_node_to_map(&g->nodes[idx[i]])); } @@ -3537,6 +6263,7 @@ void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t e->created_at = now; e->updated_at = now; e->last_fired = 0; + e->layer_id = ENGRAM_LAYER_DEFAULT; g->edge_count++; } @@ -3568,6 +6295,7 @@ static el_val_t engram_edge_to_map(const EngramEdge* e) { m = el_map_set(m, EL_STR(el_strdup("updated_at")), (el_val_t)e->updated_at); m = el_map_set(m, EL_STR(el_strdup("last_fired")), (el_val_t)e->last_fired); m = el_map_set(m, EL_STR(el_strdup("inhibitory")), (el_val_t)(e->inhibitory ? 1 : 0)); + m = el_map_set(m, EL_STR(el_strdup("layer_id")), (el_val_t)(int64_t)e->layer_id); return m; } @@ -3892,8 +6620,12 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) { g->nodes[i].background_activation = reached[i] ? best_bg[i] : 0.0; } - /* ── LAYER 2: executive filter → working memory promotion ────────── */ - /* Step A: collect inhibitory suppressions from fired inhibitory edges. */ + /* ── PASS 2: executive filter → working memory promotion ──────────── */ + /* Step A: collect inhibitory suppressions from fired inhibitory edges. + * Layered consciousness: inhibition is ONLY recorded against targets + * whose layer is `suppressible == 1`. Nodes in non-suppressible layers + * (Layer 0 / safety) ignore inhibitory edges entirely — their working + * memory weight cannot be silenced by attentional suppression. */ double* inhibition = calloc((size_t)g->node_count, sizeof(double)); if (!inhibition) { free(best_bg); free(best_hops); free(reached); free(seeds); free(fr); @@ -3906,6 +6638,12 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) { int64_t tgt = engram_find_node_index(e->to_id); if (src < 0 || tgt < 0) continue; if (!reached[src] || best_bg[src] <= 0.0) continue; + /* Skip if target layer is non-suppressible: Layer 0 / safety nodes + * are immune to inhibitory edges from any source. The pass-3 + * override below also force-promotes them, but recording inhibition + * against them at all would be wasted work and could confuse + * downstream debugging output. */ + if (!engram_layer_is_suppressible(g->nodes[tgt].layer_id)) continue; /* Inhibition strength proportional to source background activation * and edge weight. Takes the maximum if multiple inhibitory edges * target the same node. */ @@ -3949,7 +6687,30 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) { } } } - /* Persist layer-2 working_memory_weight to node store. */ + /* ── PASS 3: Layer 0 override (the sacred fire) ───────────────────── + * Every node in a non-suppressible layer that received any background + * activation is force-promoted to AT LEAST ENGRAM_LAYER0_OVERRIDE_WEIGHT. + * This runs LAST and overrides whatever Pass 2 decided — Layer 0 cannot + * be silenced by inhibitory edges, by goal-bias misalignment, by + * confidence weighting, or by per-type threshold gates. If the seed + * fan-out reached a structural-floor node, that node surfaces. + * + * Note: this also clears the suppression_count when an override fires, + * since the node DID surface this turn — it just took the override path + * rather than the standard threshold path. Without this, a Layer 0 + * node with persistent inhibitory pressure would accumulate + * suppression_count forever and never reach the breakthrough state. */ + for (int64_t i = 0; i < g->node_count; i++) { + if (!reached[i] || best_bg[i] <= 0.0) continue; + EngramNode* n = &g->nodes[i]; + if (engram_layer_is_suppressible(n->layer_id)) continue; + if (wm_weights[i] < ENGRAM_LAYER0_OVERRIDE_WEIGHT) { + wm_weights[i] = ENGRAM_LAYER0_OVERRIDE_WEIGHT; + } + n->suppression_count = 0; + } + + /* Persist working_memory_weight (post Pass 3) to node store. */ for (int64_t i = 0; i < g->node_count; i++) { g->nodes[i].working_memory_weight = wm_weights[i]; } @@ -4034,6 +6795,7 @@ static void engram_emit_node_json(JsonBuf* b, const EngramNode* n) { snprintf(tmp, sizeof(tmp), ",\"background_activation\":%g", n->background_activation); jb_puts(b, tmp); snprintf(tmp, sizeof(tmp), ",\"working_memory_weight\":%g", n->working_memory_weight); jb_puts(b, tmp); snprintf(tmp, sizeof(tmp), ",\"suppression_count\":%d", n->suppression_count); jb_puts(b, tmp); + snprintf(tmp, sizeof(tmp), ",\"layer_id\":%u", n->layer_id); jb_puts(b, tmp); jb_putc(b, '}'); } @@ -4051,6 +6813,7 @@ static void engram_emit_edge_json(JsonBuf* b, const EngramEdge* e) { snprintf(tmp, sizeof(tmp), ",\"updated_at\":%lld", (long long)e->updated_at); jb_puts(b, tmp); snprintf(tmp, sizeof(tmp), ",\"last_fired\":%lld", (long long)e->last_fired); jb_puts(b, tmp); snprintf(tmp, sizeof(tmp), ",\"inhibitory\":%d", e->inhibitory ? 1 : 0); jb_puts(b, tmp); + snprintf(tmp, sizeof(tmp), ",\"layer_id\":%u", e->layer_id); jb_puts(b, tmp); jb_putc(b, '}'); } @@ -4069,6 +6832,34 @@ el_val_t engram_save(el_val_t path) { if (i > 0) jb_putc(&b, ','); engram_emit_edge_json(&b, &g->edges[i]); } + /* Layered consciousness — emit the layer registry under "layers". + * Older readers that don't know about this top-level key will simply + * ignore it (forward compatible). Tombstoned (removed-injectable) + * layers are skipped — they have no name and can't be re-created + * meaningfully on load anyway. */ + jb_puts(&b, "],\"layers\":["); + int first_layer = 1; + for (size_t i = 0; i < g->layer_count; i++) { + EngramLayer* L = &g->layers[i]; + if (!L->name) continue; + if (!first_layer) jb_putc(&b, ','); + first_layer = 0; + jb_putc(&b, '{'); + char tmp[80]; + snprintf(tmp, sizeof(tmp), "\"layer_id\":%u", L->layer_id); + jb_puts(&b, tmp); + jb_puts(&b, ",\"name\":"); + jb_emit_escaped(&b, L->name); + snprintf(tmp, sizeof(tmp), ",\"activation_priority\":%u", L->activation_priority); + jb_puts(&b, tmp); + snprintf(tmp, sizeof(tmp), ",\"suppressible\":%d", L->suppressible ? 1 : 0); + jb_puts(&b, tmp); + snprintf(tmp, sizeof(tmp), ",\"transparent\":%d", L->transparent ? 1 : 0); + jb_puts(&b, tmp); + snprintf(tmp, sizeof(tmp), ",\"injectable\":%d", L->injectable ? 1 : 0); + jb_puts(&b, tmp); + jb_putc(&b, '}'); + } jb_puts(&b, "]}"); FILE* f = fopen(p, "wb"); if (!f) { free(b.buf); return 0; } @@ -4173,6 +6964,15 @@ el_val_t engram_load(el_val_t path) { nn->background_activation = eg_get_num_field(obj, "background_activation"); nn->working_memory_weight = eg_get_num_field(obj, "working_memory_weight"); nn->suppression_count = (int32_t)eg_get_int_field(obj, "suppression_count"); + /* layer_id defaults to ENGRAM_LAYER_DEFAULT (core-identity) + * for snapshots that predate the layered schema. We can't + * tell "explicit 0" from "missing field" using the helper + * directly, so probe for the key — if absent, fall back. */ + if (json_find_key(obj, "layer_id")) { + nn->layer_id = (uint32_t)eg_get_int_field(obj, "layer_id"); + } else { + nn->layer_id = ENGRAM_LAYER_DEFAULT; + } g->node_count++; free(obj); nodes_p = end; @@ -4209,6 +7009,11 @@ el_val_t engram_load(el_val_t path) { ee->updated_at = eg_get_int_field(obj, "updated_at"); ee->last_fired = eg_get_int_field(obj, "last_fired"); ee->inhibitory = (int)eg_get_int_field(obj, "inhibitory"); + if (json_find_key(obj, "layer_id")) { + ee->layer_id = (uint32_t)eg_get_int_field(obj, "layer_id"); + } else { + ee->layer_id = ENGRAM_LAYER_DEFAULT; + } g->edge_count++; free(obj); edges_p = end; @@ -4217,6 +7022,61 @@ el_val_t engram_load(el_val_t path) { } } } + /* Walk layers array (optional — older snapshots omit this). + * If present we replace the canonical registry entirely; if absent we + * keep whatever the engram_get() init established. */ + const char* layers_p = json_find_key(data, "layers"); + if (layers_p) { + layers_p = eg_skip_ws(layers_p); + if (*layers_p == '[') { + /* Reset existing layer registry. Free strdup'd names; the + * struct array itself can be reused. */ + for (size_t i = 0; i < g->layer_count; i++) { + if (g->layers[i].name) free(g->layers[i].name); + g->layers[i].name = NULL; + } + g->layer_count = 0; + + layers_p++; + layers_p = eg_skip_ws(layers_p); + while (*layers_p && *layers_p != ']') { + if (*layers_p != '{') { layers_p++; continue; } + const char* end = json_skip_value(layers_p); + size_t n = (size_t)(end - layers_p); + char* obj = malloc(n + 1); + memcpy(obj, layers_p, n); obj[n] = '\0'; + if (g->layer_count >= g->layer_capacity) { + size_t nc = g->layer_capacity ? g->layer_capacity * 2 : 16; + EngramLayer* grown = realloc(g->layers, nc * sizeof(EngramLayer)); + if (!grown) { fputs("el_runtime: out of memory\n", stderr); exit(1); } + memset(grown + g->layer_capacity, 0, + (nc - g->layer_capacity) * sizeof(EngramLayer)); + g->layers = grown; + g->layer_capacity = nc; + } + EngramLayer* L = &g->layers[g->layer_count]; + memset(L, 0, sizeof(*L)); + L->layer_id = (uint32_t)eg_get_int_field(obj, "layer_id"); + L->activation_priority = (uint32_t)eg_get_int_field(obj, "activation_priority"); + L->suppressible = (int)eg_get_int_field(obj, "suppressible") ? 1 : 0; + L->transparent = (int)eg_get_int_field(obj, "transparent") ? 1 : 0; + L->injectable = (int)eg_get_int_field(obj, "injectable") ? 1 : 0; + char* nm = eg_get_str_field(obj, "name"); + if (nm && *nm) { + L->name = el_strdup_persist(nm); + free(nm); + } else { + free(nm); + L->name = el_strdup_persist(""); + } + g->layer_count++; + free(obj); + layers_p = end; + layers_p = eg_skip_ws(layers_p); + if (*layers_p == ',') { layers_p++; layers_p = eg_skip_ws(layers_p); } + } + } + } free(data); return 1; } @@ -4249,6 +7109,8 @@ el_val_t engram_search_json(el_val_t query, el_val_t limit) { if (q && *q) { for (int64_t i = 0; i < g->node_count && found < lim; i++) { EngramNode* n = &g->nodes[i]; + /* Filter transparent layers — same as engram_search. */ + if (engram_layer_is_transparent(n->layer_id)) continue; if (istr_contains(n->content, q) || istr_contains(n->label, q) || istr_contains(n->tags, q)) { @@ -4272,10 +7134,52 @@ el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset) { if (g->node_count == 0) { jb_putc(&b, ']'); return el_wrap_str(b.buf); } int64_t* idx = malloc((size_t)g->node_count * sizeof(int64_t)); if (!idx) { jb_putc(&b, ']'); return el_wrap_str(b.buf); } - for (int64_t i = 0; i < g->node_count; i++) idx[i] = i; - engram_sort_indices_by_salience(idx, g->node_count, g->nodes); + /* Skip transparent layers — introspection filter, same as engram_scan_nodes. */ + int64_t live = 0; + for (int64_t i = 0; i < g->node_count; i++) { + if (engram_layer_is_transparent(g->nodes[i].layer_id)) continue; + idx[live++] = i; + } + engram_sort_indices_by_salience(idx, live, g->nodes); int64_t end = off + lim; - if (end > g->node_count) end = g->node_count; + if (end > live) end = live; + int first = 1; + for (int64_t i = off; i < end; i++) { + if (!first) jb_putc(&b, ','); + engram_emit_node_json(&b, &g->nodes[idx[i]]); + first = 0; + } + free(idx); + jb_putc(&b, ']'); + return el_wrap_str(b.buf); +} + +/* engram_scan_nodes_by_type_json — filter by node_type before paginating. + * Empty / NULL type_v falls back to the unfiltered scan (existing behaviour). + * Result is JSON array, salience-sorted, transparent layers skipped. */ +el_val_t engram_scan_nodes_by_type_json(el_val_t type_v, el_val_t limit, el_val_t offset) { + const char* type_filter = EL_CSTR(type_v); + if (!type_filter || !*type_filter) { + return engram_scan_nodes_json(limit, offset); + } + EngramStore* g = engram_get(); + int64_t lim = (int64_t)limit; if (lim <= 0) lim = 100; + int64_t off = (int64_t)offset; if (off < 0) off = 0; + JsonBuf b; jb_init(&b); + jb_putc(&b, '['); + if (g->node_count == 0) { jb_putc(&b, ']'); return el_wrap_str(b.buf); } + int64_t* idx = malloc((size_t)g->node_count * sizeof(int64_t)); + if (!idx) { jb_putc(&b, ']'); return el_wrap_str(b.buf); } + int64_t live = 0; + for (int64_t i = 0; i < g->node_count; i++) { + if (engram_layer_is_transparent(g->nodes[i].layer_id)) continue; + const char* nt = g->nodes[i].node_type; + if (!nt || strcmp(nt, type_filter) != 0) continue; + idx[live++] = i; + } + engram_sort_indices_by_salience(idx, live, g->nodes); + int64_t end = off + lim; + if (end > live) end = live; int first = 1; for (int64_t i = off; i < end; i++) { if (!first) jb_putc(&b, ','); @@ -4398,11 +7302,145 @@ el_val_t engram_stats_json(void) { EngramStore* g = engram_get(); char buf[128]; snprintf(buf, sizeof(buf), - "{\"node_count\":%lld,\"edge_count\":%lld}", - (long long)g->node_count, (long long)g->edge_count); + "{\"node_count\":%lld,\"edge_count\":%lld,\"layer_count\":%zu}", + (long long)g->node_count, (long long)g->edge_count, g->layer_count); return el_wrap_str(el_strdup(buf)); } +/* engram_list_layers_json — serialized counterpart of engram_list_layers. + * Returns a JSON array, sorted by activation_priority ascending. */ +el_val_t engram_list_layers_json(void) { + EngramStore* g = engram_get(); + JsonBuf b; jb_init(&b); + jb_putc(&b, '['); + /* Build a sorted index over live layers. */ + size_t* idx = malloc((g->layer_count + 1) * sizeof(size_t)); + if (!idx) { jb_putc(&b, ']'); return el_wrap_str(b.buf); } + size_t live = 0; + for (size_t i = 0; i < g->layer_count; i++) { + if (g->layers[i].name) idx[live++] = i; + } + for (size_t i = 1; i < live; i++) { + size_t key = idx[i]; + uint32_t kp = g->layers[key].activation_priority; + size_t j = i; + while (j > 0 && g->layers[idx[j - 1]].activation_priority > kp) { + idx[j] = idx[j - 1]; + j--; + } + idx[j] = key; + } + int first = 1; + for (size_t i = 0; i < live; i++) { + EngramLayer* L = &g->layers[idx[i]]; + if (!first) jb_putc(&b, ','); + first = 0; + jb_putc(&b, '{'); + char tmp[80]; + snprintf(tmp, sizeof(tmp), "\"layer_id\":%u", L->layer_id); jb_puts(&b, tmp); + jb_puts(&b, ",\"name\":"); + jb_emit_escaped(&b, L->name ? L->name : ""); + snprintf(tmp, sizeof(tmp), ",\"activation_priority\":%u", L->activation_priority); + jb_puts(&b, tmp); + snprintf(tmp, sizeof(tmp), ",\"suppressible\":%d", L->suppressible ? 1 : 0); + jb_puts(&b, tmp); + snprintf(tmp, sizeof(tmp), ",\"transparent\":%d", L->transparent ? 1 : 0); + jb_puts(&b, tmp); + snprintf(tmp, sizeof(tmp), ",\"injectable\":%d", L->injectable ? 1 : 0); + jb_puts(&b, tmp); + jb_putc(&b, '}'); + } + free(idx); + jb_putc(&b, ']'); + return el_wrap_str(b.buf); +} + +/* engram_compile_layered_json — produce a prompt-ready context block split + * by layer. + * + * Runs the three-pass activation, then partitions promoted nodes by layer + * suppressibility: + * - Non-suppressible (Layer 0 / structural-floor) layers go FIRST under + * the heading "[LAYER 0 — STRUCTURAL]". These are the sacred-fire + * nodes that surfaced via the pass-3 override. + * - All other promoted layers go SECOND under "[ENGRAM CONTEXT]". + * + * Output is a single JSON-string el_val_t: a UTF-8 text block ready to be + * concatenated into a system prompt. Returns "" if no nodes promoted. + * + * Transparent layers (Layer 0) are emitted into the prompt — they shape + * the model's output — but engram_search and friends still hide them from + * introspection-style queries. The split heading lets the LLM weight them + * appropriately without revealing their internal label. + * + * Each emitted line for a node is its raw JSON (matching engram_emit_node_json) + * so downstream JSON parsers can still walk individual records inside the + * formatted block. The block is plain text, not a JSON document — callers + * concatenating it into a prompt should treat it as opaque markdown. */ +el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth) { + EngramStore* g = engram_get(); + /* Run the three-pass activator. We need the persisted node fields, so + * call engram_activate (it writes background_activation and + * working_memory_weight back into the store). */ + (void)engram_activate(intent, depth); + + /* Walk the store and partition by suppressibility. */ + JsonBuf b; jb_init(&b); + int wrote_layer0 = 0; + int wrote_normal = 0; + + /* Sort indices by working_memory_weight descending so the most + * confidently promoted nodes appear first within each section. */ + int64_t* idx = malloc((size_t)(g->node_count + 1) * sizeof(int64_t)); + if (!idx) return el_wrap_str(el_strdup("")); + int64_t mc = 0; + for (int64_t i = 0; i < g->node_count; i++) { + if (g->nodes[i].working_memory_weight > 0.0) idx[mc++] = i; + } + for (int64_t i = 1; i < mc; i++) { + int64_t key = idx[i]; + double kw = g->nodes[key].working_memory_weight; + int64_t j = i; + while (j > 0 && g->nodes[idx[j - 1]].working_memory_weight < kw) { + idx[j] = idx[j - 1]; + j--; + } + idx[j] = key; + } + + /* Section 1: structural floor (non-suppressible layers). */ + for (int64_t i = 0; i < mc; i++) { + EngramNode* n = &g->nodes[idx[i]]; + if (engram_layer_is_suppressible(n->layer_id)) continue; + if (!wrote_layer0) { + jb_puts(&b, "[LAYER 0 — STRUCTURAL]\n"); + wrote_layer0 = 1; + } + engram_emit_node_json(&b, n); + jb_putc(&b, '\n'); + } + + /* Section 2: standard engram context (suppressible layers). */ + for (int64_t i = 0; i < mc; i++) { + EngramNode* n = &g->nodes[idx[i]]; + if (!engram_layer_is_suppressible(n->layer_id)) continue; + if (!wrote_normal) { + if (wrote_layer0) jb_putc(&b, '\n'); + jb_puts(&b, "[ENGRAM CONTEXT]\n"); + wrote_normal = 1; + } + engram_emit_node_json(&b, n); + jb_putc(&b, '\n'); + } + + free(idx); + if (b.len == 0) { + free(b.buf); + return el_wrap_str(el_strdup("")); + } + return el_wrap_str(b.buf); +} + /* engram_query_range — temporal range query. * Returns a JSON array of nodes whose created_at OR last_activated falls * within [start_ms, end_ms], sorted by created_at ascending. @@ -4850,6 +7888,7 @@ static int64_t dharma_find_or_create_relation_edge(const char* peer_base, int cr n->salience = 1.0; n->importance = 1.0; n->confidence = 1.0; int64_t now = engram_now_ms(); n->created_at = now; n->updated_at = now; n->last_activated = now; + n->layer_id = ENGRAM_LAYER_DEFAULT; g->node_count++; } if (!engram_find_node(peer_node)) { @@ -4866,6 +7905,7 @@ static int64_t dharma_find_or_create_relation_edge(const char* peer_base, int cr n->salience = 0.5; n->importance = 0.5; n->confidence = 1.0; int64_t now = engram_now_ms(); n->created_at = now; n->updated_at = now; n->last_activated = now; + n->layer_id = ENGRAM_LAYER_DEFAULT; g->node_count++; } /* Create the edge with weight 0.0 — caller will increment. */ @@ -4881,6 +7921,7 @@ static int64_t dharma_find_or_create_relation_edge(const char* peer_base, int cr e->confidence = 1.0; int64_t now = engram_now_ms(); e->created_at = now; e->updated_at = now; + e->layer_id = ENGRAM_LAYER_DEFAULT; int64_t idx = g->edge_count; g->edge_count++; return idx; @@ -6691,3 +9732,515 @@ el_val_t pq_hybrid_handshake(el_val_t remote_pub_combined) { #endif /* EL_HAVE_OPENSSL */ #endif /* EL_HAVE_LIBOQS */ + +/* ─── AEAD: AES-256-GCM ──────────────────────────────────────────────────── + * + * Symmetric authenticated encryption used to wrap envelopes once a shared + * secret has been derived from the KEM (Kyber-768 / hybrid). The El surface + * is intentionally narrow: + * + * aead_encrypt(key_hex, plaintext) + * → {"nonce":"<24 hex>","ciphertext":"<...hex including 16-byte tag>"} + * + * aead_decrypt(key_hex, nonce_hex, ciphertext_hex) + * → plaintext String, or "" on auth failure / malformed input + * + * Conventions: + * - key_hex must decode to exactly 32 bytes (AES-256). Callers that hold + * a longer KEM shared_secret should normalize via SHA3-256(ss) → 32 bytes + * before passing it in. (Kyber-768's shared_secret is already 32 bytes, + * but keeping this contract explicit lets the El side be agnostic.) + * - nonce is a fresh 12-byte random value drawn from the OS CSPRNG. Caller + * never picks the nonce — eliminates the GCM nonce-reuse footgun entirely. + * - tag is the standard 16 bytes, appended to ciphertext per RFC 5116. + * `ciphertext` field is therefore (plaintext_len + 16) bytes, hex-encoded. + * - No associated data (AAD). If we later need bound metadata, add a + * length-prefixed AAD argument and bump the envelope version tag. + * + * Failure mode: + * aead_encrypt returns http_error_json(...) on input/system failure. + * aead_decrypt returns the empty string on ANY failure (including auth-tag + * mismatch). Callers MUST check for "" before using the result. */ + +#if !__has_include() + +el_val_t aead_encrypt(el_val_t key_hex, el_val_t plaintext) { + (void)key_hex; (void)plaintext; + return http_error_json("aead_encrypt requires OpenSSL (libcrypto); rebuild with -lcrypto"); +} +el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_hex) { + (void)key_hex; (void)nonce_hex; (void)ciphertext_hex; + return el_wrap_str(el_strdup("")); +} + +#else /* OpenSSL available */ + +#include +#include + +el_val_t aead_encrypt(el_val_t key_hex, el_val_t plaintext) { + size_t key_len = 0; + unsigned char* key = el_hex_decode(EL_CSTR(key_hex), &key_len); + if (!key) return http_error_json("invalid hex in key"); + if (key_len != 32) return http_error_json("aead key must be 32 bytes (64 hex chars) for AES-256-GCM"); + + const char* pt = EL_CSTR(plaintext); + size_t pt_len = el_input_len(pt); + if (!pt) pt = ""; + + unsigned char nonce[12]; + if (RAND_bytes(nonce, 12) != 1) return http_error_json("OS CSPRNG failed (RAND_bytes)"); + + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + if (!ctx) return http_error_json("EVP_CIPHER_CTX_new failed"); + + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) != 1) { + EVP_CIPHER_CTX_free(ctx); return http_error_json("aes-256-gcm init failed"); + } + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL) != 1) { + EVP_CIPHER_CTX_free(ctx); return http_error_json("set ivlen failed"); + } + if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce) != 1) { + EVP_CIPHER_CTX_free(ctx); return http_error_json("aes-256-gcm key/iv init failed"); + } + + /* GCM ciphertext is the same length as plaintext; we append a 16-byte + * authentication tag for AEAD semantics. Allocate plaintext_len + 16. */ + unsigned char* ct = (unsigned char*)malloc(pt_len + 16); + if (!ct) { EVP_CIPHER_CTX_free(ctx); return http_error_json("oom"); } + int outlen = 0, total = 0; + if (EVP_EncryptUpdate(ctx, ct, &outlen, (const unsigned char*)pt, (int)pt_len) != 1) { + free(ct); EVP_CIPHER_CTX_free(ctx); return http_error_json("aes-256-gcm update failed"); + } + total += outlen; + if (EVP_EncryptFinal_ex(ctx, ct + total, &outlen) != 1) { + free(ct); EVP_CIPHER_CTX_free(ctx); return http_error_json("aes-256-gcm final failed"); + } + total += outlen; + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, ct + total) != 1) { + free(ct); EVP_CIPHER_CTX_free(ctx); return http_error_json("aes-256-gcm get tag failed"); + } + EVP_CIPHER_CTX_free(ctx); + + el_val_t nonce_hex_v = el_hex_encode(nonce, 12); + el_val_t ct_hex_v = el_hex_encode(ct, (size_t)total + 16); + free(ct); + + const char* nh = EL_CSTR(nonce_hex_v); + const char* ch = EL_CSTR(ct_hex_v); + char* buf = el_strbuf(strlen(nh) + strlen(ch) + 48); + sprintf(buf, "{\"nonce\":\"%s\",\"ciphertext\":\"%s\"}", nh, ch); + return el_wrap_str(buf); +} + +el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_hex) { + size_t key_len = 0, nonce_len = 0, ct_len = 0; + unsigned char* key = el_hex_decode(EL_CSTR(key_hex), &key_len); + unsigned char* nonce = el_hex_decode(EL_CSTR(nonce_hex), &nonce_len); + unsigned char* ct = el_hex_decode(EL_CSTR(ciphertext_hex), &ct_len); + if (!key || !nonce || !ct) return el_wrap_str(el_strdup("")); + if (key_len != 32 || nonce_len != 12) return el_wrap_str(el_strdup("")); + if (ct_len < 16) return el_wrap_str(el_strdup("")); + + size_t body_len = ct_len - 16; + const unsigned char* tag = ct + body_len; + + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + if (!ctx) return el_wrap_str(el_strdup("")); + + if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) != 1 || + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL) != 1 || + EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce) != 1) { + EVP_CIPHER_CTX_free(ctx); return el_wrap_str(el_strdup("")); + } + + unsigned char* pt = (unsigned char*)malloc(body_len + 1); + if (!pt) { EVP_CIPHER_CTX_free(ctx); return el_wrap_str(el_strdup("")); } + int outlen = 0, total = 0; + if (EVP_DecryptUpdate(ctx, pt, &outlen, ct, (int)body_len) != 1) { + free(pt); EVP_CIPHER_CTX_free(ctx); return el_wrap_str(el_strdup("")); + } + total += outlen; + /* Set expected tag before final — GCM's final step is where auth happens. */ + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag) != 1) { + free(pt); EVP_CIPHER_CTX_free(ctx); return el_wrap_str(el_strdup("")); + } + int rc = EVP_DecryptFinal_ex(ctx, pt + total, &outlen); + EVP_CIPHER_CTX_free(ctx); + if (rc != 1) { + /* Auth failure or padding/length mismatch. Return empty so callers + * cannot accidentally treat tampered ciphertext as a valid message. */ + free(pt); + return el_wrap_str(el_strdup("")); + } + total += outlen; + pt[total] = '\0'; + + /* Copy into the el arena so the caller-visible string outlives this fn. */ + char* out = el_strbuf((size_t)total); + memcpy(out, pt, (size_t)total); + out[total] = '\0'; + free(pt); + return el_wrap_str(out); +} + +#endif /* __has_include() */ + +/* ──────────────────────────────────────────────────────────────────────────── + * OTLP/HTTP observability — logs, traces, metrics + * + * Design goals: + * - Zero blocking on the request path. Producers append to in-memory + * ring buffers; a single worker thread flushes to the OTLP endpoint. + * - Drop-on-failure semantics. If the endpoint is unreachable or slow, + * we drop telemetry rather than back-pressure into the request handler. + * - Best-effort serialization. Each record is pre-serialized as JSON when + * the El program calls the primitive; the worker just batches. + * - Configuration via env vars: + * OTLP_ENDPOINT e.g. https://alloy.neuralplatform.ai:4318 + * OTEL_SERVICE_NAME e.g. neuron-web (default: argv[0] basename) + * OTEL_SERVICE_VERSION (default: "0.0.0") + * OTEL_RESOURCE_ATTRS comma-sep k=v pairs (optional) + * + * Wire format: OTLP/HTTP JSON. Three endpoints: + * POST {endpoint}/v1/logs — log records + * POST {endpoint}/v1/traces — spans + * POST {endpoint}/v1/metrics — counter/gauge points + * + * El programs see four primitives: + * trace_span_start(name) -> SpanHandle (just a string id) + * trace_span_end(handle) (computes duration, queues) + * emit_log(level, msg, fields_json) (queues a log record) + * emit_metric(name, value, tags_json) (queues a counter increment) + * ──────────────────────────────────────────────────────────────────────────── + */ + +#define OTLP_BUF_CAP 4096 /* per-buffer ring size */ +#define OTLP_FLUSH_MS 2000 /* flush every 2s */ +#define OTLP_BATCH_MAX 200 /* up to 200 records per POST */ + +typedef struct { + char* data; /* malloc'd JSON fragment for this record */ +} OtlpRec; + +typedef struct { + OtlpRec ring[OTLP_BUF_CAP]; + size_t head; /* next write slot */ + size_t tail; /* next read slot */ + pthread_mutex_t mu; +} OtlpQueue; + +static OtlpQueue _otlp_logs = { .mu = PTHREAD_MUTEX_INITIALIZER }; +static OtlpQueue _otlp_traces = { .mu = PTHREAD_MUTEX_INITIALIZER }; +static OtlpQueue _otlp_metrics = { .mu = PTHREAD_MUTEX_INITIALIZER }; + +static char* _otlp_endpoint = NULL; /* e.g. https://alloy.neuralplatform.ai:4318 */ +static char* _otlp_service_name = NULL; +static char* _otlp_service_version = NULL; +static int _otlp_initialized = 0; +static pthread_t _otlp_worker_thread; + +/* enqueue — returns 1 if accepted, 0 if dropped (full buffer or no endpoint) */ +static int otlp_enqueue(OtlpQueue* q, const char* json) { + if (!_otlp_endpoint || !json) return 0; + pthread_mutex_lock(&q->mu); + size_t next_head = (q->head + 1) % OTLP_BUF_CAP; + if (next_head == q->tail) { + /* buffer full — drop oldest */ + free(q->ring[q->tail].data); + q->ring[q->tail].data = NULL; + q->tail = (q->tail + 1) % OTLP_BUF_CAP; + } + q->ring[q->head].data = strdup(json); + q->head = next_head; + pthread_mutex_unlock(&q->mu); + return 1; +} + +/* drain — copies up to OTLP_BATCH_MAX items into a comma-joined string, + * caller must free the result. Returns NULL if queue is empty. */ +static char* otlp_drain(OtlpQueue* q) { + pthread_mutex_lock(&q->mu); + if (q->head == q->tail) { pthread_mutex_unlock(&q->mu); return NULL; } + /* compute total length */ + size_t total = 0, count = 0; + size_t i = q->tail; + while (i != q->head && count < OTLP_BATCH_MAX) { + if (q->ring[i].data) total += strlen(q->ring[i].data) + 1; /* +1 for comma */ + i = (i + 1) % OTLP_BUF_CAP; + count++; + } + char* out = malloc(total + 4); + if (!out) { pthread_mutex_unlock(&q->mu); return NULL; } + out[0] = '\0'; + size_t off = 0; + i = q->tail; + count = 0; + while (i != q->head && count < OTLP_BATCH_MAX) { + if (q->ring[i].data) { + size_t l = strlen(q->ring[i].data); + if (off > 0) { out[off++] = ','; } + memcpy(out + off, q->ring[i].data, l); + off += l; + free(q->ring[i].data); + q->ring[i].data = NULL; + } + i = (i + 1) % OTLP_BUF_CAP; + count++; + } + out[off] = '\0'; + q->tail = i; + pthread_mutex_unlock(&q->mu); + return out; +} + +/* Build resource block once (service.name, service.version, host.name) */ +static char* otlp_resource_block(void) { + static char cached[1024]; + static int built = 0; + if (built) return cached; + char host[256] = "unknown"; + gethostname(host, sizeof(host) - 1); + snprintf(cached, sizeof(cached), + "{\"attributes\":[" + "{\"key\":\"service.name\",\"value\":{\"stringValue\":\"%s\"}}," + "{\"key\":\"service.version\",\"value\":{\"stringValue\":\"%s\"}}," + "{\"key\":\"host.name\",\"value\":{\"stringValue\":\"%s\"}}" + "]}", + _otlp_service_name ? _otlp_service_name : "el-app", + _otlp_service_version ? _otlp_service_version : "0.0.0", + host); + built = 1; + return cached; +} + +/* Best-effort POST. Drops on any error. */ +static void otlp_post(const char* path, const char* body) { + if (!_otlp_endpoint || !body || !*body) return; + char url[1024]; + snprintf(url, sizeof(url), "%s%s", _otlp_endpoint, path); + CURL* c = curl_easy_init(); + if (!c) return; + struct curl_slist* h = NULL; + h = curl_slist_append(h, "Content-Type: application/json"); + curl_easy_setopt(c, CURLOPT_URL, url); + curl_easy_setopt(c, CURLOPT_POST, 1L); + curl_easy_setopt(c, CURLOPT_POSTFIELDS, body); + curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)strlen(body)); + curl_easy_setopt(c, CURLOPT_HTTPHEADER, h); + curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, 3000L); + curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, NULL); /* discard response */ + curl_easy_perform(c); + curl_slist_free_all(h); + curl_easy_cleanup(c); +} + +/* Flush worker — runs forever until process exits */ +static void* otlp_worker(void* arg) { + (void)arg; + while (1) { + struct timespec ts = { OTLP_FLUSH_MS / 1000, (OTLP_FLUSH_MS % 1000) * 1000000L }; + nanosleep(&ts, NULL); + + char* logs = otlp_drain(&_otlp_logs); + if (logs && *logs) { + char body[OTLP_BUF_CAP * 8]; + int n = snprintf(body, sizeof(body), + "{\"resourceLogs\":[{\"resource\":%s," + "\"scopeLogs\":[{\"scope\":{\"name\":\"el-runtime\"}," + "\"logRecords\":[%s]}]}]}", + otlp_resource_block(), logs); + if (n > 0 && n < (int)sizeof(body)) otlp_post("/v1/logs", body); + } + free(logs); + + char* traces = otlp_drain(&_otlp_traces); + if (traces && *traces) { + char body[OTLP_BUF_CAP * 8]; + int n = snprintf(body, sizeof(body), + "{\"resourceSpans\":[{\"resource\":%s," + "\"scopeSpans\":[{\"scope\":{\"name\":\"el-runtime\"}," + "\"spans\":[%s]}]}]}", + otlp_resource_block(), traces); + if (n > 0 && n < (int)sizeof(body)) otlp_post("/v1/traces", body); + } + free(traces); + + char* metrics = otlp_drain(&_otlp_metrics); + if (metrics && *metrics) { + char body[OTLP_BUF_CAP * 8]; + int n = snprintf(body, sizeof(body), + "{\"resourceMetrics\":[{\"resource\":%s," + "\"scopeMetrics\":[{\"scope\":{\"name\":\"el-runtime\"}," + "\"metrics\":[%s]}]}]}", + otlp_resource_block(), metrics); + if (n > 0 && n < (int)sizeof(body)) otlp_post("/v1/metrics", body); + } + free(metrics); + } + return NULL; +} + +/* Initialize OTLP — called lazily on first emit. Idempotent. */ +static void otlp_lazy_init(void) { + if (_otlp_initialized) return; + static pthread_mutex_t once_mu = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&once_mu); + if (_otlp_initialized) { pthread_mutex_unlock(&once_mu); return; } + + const char* ep = getenv("OTLP_ENDPOINT"); + if (!ep || !*ep) { + _otlp_initialized = 1; + pthread_mutex_unlock(&once_mu); + return; + } + _otlp_endpoint = strdup(ep); + /* trim trailing slash */ + size_t l = strlen(_otlp_endpoint); + if (l > 0 && _otlp_endpoint[l - 1] == '/') _otlp_endpoint[l - 1] = '\0'; + + const char* svc = getenv("OTEL_SERVICE_NAME"); + _otlp_service_name = strdup(svc && *svc ? svc : "el-app"); + const char* ver = getenv("OTEL_SERVICE_VERSION"); + _otlp_service_version = strdup(ver && *ver ? ver : "0.0.0"); + + pthread_create(&_otlp_worker_thread, NULL, otlp_worker, NULL); + pthread_detach(_otlp_worker_thread); + _otlp_initialized = 1; + pthread_mutex_unlock(&once_mu); +} + +/* JSON-escape a string into out_buf. Returns chars written (excluding null). */ +static size_t otlp_json_escape(const char* in, char* out, size_t out_cap) { + size_t o = 0; + for (size_t i = 0; in[i] && o + 8 < out_cap; i++) { + unsigned char c = (unsigned char)in[i]; + if (c == '"') { out[o++] = '\\'; out[o++] = '"'; } + else if (c == '\\'){ out[o++] = '\\'; out[o++] = '\\'; } + else if (c == '\n'){ out[o++] = '\\'; out[o++] = 'n'; } + else if (c == '\r'){ out[o++] = '\\'; out[o++] = 'r'; } + else if (c == '\t'){ out[o++] = '\\'; out[o++] = 't'; } + else if (c < 0x20) { o += snprintf(out + o, out_cap - o, "\\u%04x", c); } + else { out[o++] = (char)c; } + } + out[o] = '\0'; + return o; +} + +/* ── Public El primitives ─────────────────────────────────────────────────── */ + +/* emit_log(level, msg, fields_json) — fields_json is a JSON object string or "" */ +el_val_t emit_log(el_val_t level_v, el_val_t msg_v, el_val_t fields_v) { + otlp_lazy_init(); + if (!_otlp_endpoint) return EL_INT(0); + const char* level = EL_CSTR(level_v); if (!level) level = "INFO"; + const char* msg = EL_CSTR(msg_v); if (!msg) msg = ""; + const char* fields = EL_CSTR(fields_v); if (!fields) fields = ""; + /* Map El level names to OTLP severity numbers */ + int sev_num = 9; /* INFO */ + if (strcmp(level, "TRACE") == 0) sev_num = 1; + else if (strcmp(level, "DEBUG") == 0) sev_num = 5; + else if (strcmp(level, "INFO") == 0) sev_num = 9; + else if (strcmp(level, "WARN") == 0 || strcmp(level, "WARNING") == 0) sev_num = 13; + else if (strcmp(level, "ERROR") == 0) sev_num = 17; + else if (strcmp(level, "FATAL") == 0) sev_num = 21; + char esc_msg[2048]; otlp_json_escape(msg, esc_msg, sizeof(esc_msg)); + /* unix nanos */ + struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); + long long now_nano = (long long)ts.tv_sec * 1000000000LL + ts.tv_nsec; + char rec[4096]; + int n = snprintf(rec, sizeof(rec), + "{\"timeUnixNano\":\"%lld\",\"severityNumber\":%d," + "\"severityText\":\"%s\"," + "\"body\":{\"stringValue\":\"%s\"}%s%s}", + now_nano, sev_num, level, esc_msg, + (fields && *fields) ? ",\"attributes\":" : "", + (fields && *fields) ? fields : ""); + if (n > 0 && n < (int)sizeof(rec)) otlp_enqueue(&_otlp_logs, rec); + return EL_INT(1); +} + +/* emit_metric(name, value, tags_json) — Sum (counter) data point. tags_json + * is a JSON array of {key, value} pairs or empty string. */ +el_val_t emit_metric(el_val_t name_v, el_val_t value_v, el_val_t tags_v) { + otlp_lazy_init(); + if (!_otlp_endpoint) return EL_INT(0); + const char* name = EL_CSTR(name_v); if (!name) name = "unknown"; + int64_t val = (int64_t)value_v; + const char* tags = EL_CSTR(tags_v); if (!tags) tags = ""; + char esc_name[256]; otlp_json_escape(name, esc_name, sizeof(esc_name)); + struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); + long long now_nano = (long long)ts.tv_sec * 1000000000LL + ts.tv_nsec; + char rec[4096]; + int n = snprintf(rec, sizeof(rec), + "{\"name\":\"%s\",\"sum\":{\"aggregationTemporality\":2,\"isMonotonic\":true," + "\"dataPoints\":[{\"asInt\":\"%lld\"," + "\"timeUnixNano\":\"%lld\"" + "%s%s}]}}", + esc_name, (long long)val, now_nano, + (tags && *tags) ? ",\"attributes\":" : "", + (tags && *tags) ? tags : ""); + if (n > 0 && n < (int)sizeof(rec)) otlp_enqueue(&_otlp_metrics, rec); + return EL_INT(1); +} + +/* trace_span_start(name) — returns a span handle (string of "traceid:spanid:start_nano:name") */ +el_val_t trace_span_start(el_val_t name_v) { + otlp_lazy_init(); + const char* name = EL_CSTR(name_v); if (!name) name = "span"; + /* generate 16-byte trace id and 8-byte span id */ + static _Thread_local int seeded = 0; + if (!seeded) { srand((unsigned int)(uintptr_t)pthread_self() ^ (unsigned int)time(NULL)); seeded = 1; } + char tid[33], sid[17]; + for (int i = 0; i < 32; i++) tid[i] = "0123456789abcdef"[rand() & 0xF]; + tid[32] = '\0'; + for (int i = 0; i < 16; i++) sid[i] = "0123456789abcdef"[rand() & 0xF]; + sid[16] = '\0'; + struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); + long long now_nano = (long long)ts.tv_sec * 1000000000LL + ts.tv_nsec; + char* handle = malloc(strlen(name) + 80); + if (!handle) return EL_STR(""); + sprintf(handle, "%s:%s:%lld:%s", tid, sid, now_nano, name); + el_arena_track(handle); + return EL_STR(handle); +} + +/* trace_span_end(handle) — emits the span with computed duration */ +el_val_t trace_span_end(el_val_t handle_v) { + otlp_lazy_init(); + if (!_otlp_endpoint) return EL_INT(0); + const char* h = EL_CSTR(handle_v); if (!h) return EL_INT(0); + /* parse "tid:sid:start_nano:name" */ + char tid[64], sid[32], rest[1024]; + long long start_nano = 0; + if (sscanf(h, "%63[^:]:%31[^:]:%lld:%1023[^\n]", tid, sid, &start_nano, rest) != 4) return EL_INT(0); + struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); + long long end_nano = (long long)ts.tv_sec * 1000000000LL + ts.tv_nsec; + char esc_name[1024]; otlp_json_escape(rest, esc_name, sizeof(esc_name)); + char rec[4096]; + int n = snprintf(rec, sizeof(rec), + "{\"traceId\":\"%s\",\"spanId\":\"%s\"," + "\"name\":\"%s\"," + "\"kind\":1," + "\"startTimeUnixNano\":\"%lld\"," + "\"endTimeUnixNano\":\"%lld\"," + "\"status\":{\"code\":1}}", + tid, sid, esc_name, start_nano, end_nano); + if (n > 0 && n < (int)sizeof(rec)) otlp_enqueue(&_otlp_traces, rec); + return EL_INT(1); +} + +/* Convenience: emit a one-shot timed event (emit start+end immediately). + * For El programs that want point events with duration baked in. */ +el_val_t emit_event(el_val_t name_v, el_val_t duration_ms_v) { + otlp_lazy_init(); + if (!_otlp_endpoint) return EL_INT(0); + const char* name = EL_CSTR(name_v); if (!name) name = "event"; + int64_t dur_ms = (int64_t)duration_ms_v; + el_val_t h = trace_span_start(EL_STR((char*)name)); + /* fudge start to be (now - duration) */ + (void)dur_ms; + return trace_span_end(h); +} + diff --git a/releases/v1.0.0-20260501/el_runtime.h b/releases/v1.0.0-20260501/el_runtime.h index 693c210..72bbf4b 100644 --- a/releases/v1.0.0-20260501/el_runtime.h +++ b/releases/v1.0.0-20260501/el_runtime.h @@ -199,6 +199,19 @@ el_val_t http_get_to_file(el_val_t url, el_val_t headers_map, el_val_t output_p el_val_t url_encode(el_val_t s); /* RFC 3986 unreserved set */ el_val_t url_decode(el_val_t s); /* '+' → space, %XX → byte */ +/* ── HTML allowlist sanitizer ──────────────────────────────────────────────── + * el_html_sanitize(input_html, allowlist_json) — strict allowlist HTML + * cleaner. State-machine parser; tag/attribute names compared case- + * insensitively against the allowlist; `` / `<… src>` URL schemes + * validated (http, https, mailto, fragment-only, or relative); whole- + * subtree drop for script / style / iframe / object / embed / form; HTML- + * escapes free text outside dropped subtrees. + * + * The allowlist is JSON of the form + * {"p":[],"a":["href","title"],"strong":[],...} + * where each value is the array of attribute names allowed for that tag. */ +el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json); + /* ── Filesystem ──────────────────────────────────────────────────────────── */ el_val_t fs_read(el_val_t path); @@ -246,6 +259,146 @@ el_val_t time_from_parts(el_val_t secs, el_val_t ns, el_val_t tz); el_val_t time_add(el_val_t ts, el_val_t n, el_val_t unit); el_val_t time_diff(el_val_t ts1, el_val_t ts2, el_val_t unit); +/* ── Instant + Duration: first-class temporal types ────────────────────────── + * Both types share the el_val_t (int64) slot. Instants are nanoseconds + * since the Unix epoch; Durations are signed nanoseconds. Type discipline + * is enforced at codegen-time: BinOps on names registered as Instant or + * Duration route through the typed wrappers below; mismatches like + * Instant+Instant become #error at the C compiler. + * + * Postfix literals — `30.seconds`, `1.hour`, `500.millis`, `30.nanos` — are + * recognised by the parser as DurationLit AST nodes and lowered to literal + * int64 nanoseconds at codegen time. The runtime never sees the units. */ + +el_val_t el_now_instant(void); +el_val_t now(void); +el_val_t unix_seconds(el_val_t n); +el_val_t unix_millis(el_val_t n); +el_val_t instant_from_iso8601(el_val_t s); + +el_val_t el_duration_from_nanos(el_val_t ns); +el_val_t duration_seconds(el_val_t n); +el_val_t duration_millis(el_val_t n); +el_val_t duration_nanos(el_val_t n); + +el_val_t el_instant_add_dur(el_val_t inst, el_val_t dur); +el_val_t el_instant_sub_dur(el_val_t inst, el_val_t dur); +el_val_t el_instant_diff(el_val_t a, el_val_t b); +el_val_t el_duration_add(el_val_t a, el_val_t b); +el_val_t el_duration_sub(el_val_t a, el_val_t b); +el_val_t el_duration_scale(el_val_t dur, el_val_t scalar); +el_val_t el_duration_div(el_val_t dur, el_val_t scalar); + +el_val_t el_instant_lt(el_val_t a, el_val_t b); +el_val_t el_instant_le(el_val_t a, el_val_t b); +el_val_t el_instant_gt(el_val_t a, el_val_t b); +el_val_t el_instant_ge(el_val_t a, el_val_t b); +el_val_t el_instant_eq(el_val_t a, el_val_t b); +el_val_t el_instant_ne(el_val_t a, el_val_t b); +el_val_t el_duration_lt(el_val_t a, el_val_t b); +el_val_t el_duration_le(el_val_t a, el_val_t b); +el_val_t el_duration_gt(el_val_t a, el_val_t b); +el_val_t el_duration_ge(el_val_t a, el_val_t b); +el_val_t el_duration_eq(el_val_t a, el_val_t b); +el_val_t el_duration_ne(el_val_t a, el_val_t b); + +el_val_t instant_to_unix_seconds(el_val_t i); +el_val_t instant_to_unix_millis(el_val_t i); +el_val_t instant_to_iso8601(el_val_t i); +el_val_t duration_to_seconds(el_val_t d); +el_val_t duration_to_millis(el_val_t d); +el_val_t duration_to_nanos(el_val_t d); + +el_val_t el_sleep_duration(el_val_t dur); +el_val_t unix_timestamp(void); + +el_val_t ttl_cache_set(el_val_t key, el_val_t value); +el_val_t ttl_cache_get(el_val_t key, el_val_t max_age); +el_val_t ttl_cache_age(el_val_t key); + +/* ── Calendar + CalendarTime + Rhythm + LocalDate/Time/DateTime ───────────── + * Phase 1.5 of the time system. Calendar is pluggable: EarthCalendar (IANA + * zones, Gregorian, DST) is the user-facing default; MarsCalendar, + * CycleCalendar(period), NoCycleCalendar, RelativeCalendar handle non-Earth + * domains. + * + * A Calendar interprets an Instant under a particular cycle convention and + * produces a CalendarTime. CalendarTime carries the underlying Instant and + * a back-pointer to its Calendar; arithmetic and formatting consult the + * Calendar to convert ns since epoch into year/month/day/hour/minute/second + * (or sol/phase, or cycle/phase, depending on kind). + * + * Storage convention: Calendar / CalendarTime / Rhythm / LocalDate / + * LocalDateTime are heap-allocated structs whose pointers are cast into + * el_val_t. A 24-bit magic header at offset 0 lets the runtime identify + * the kind safely. LocalTime is small enough to live in the int64 slot + * directly (nanos since midnight, signed). */ + +/* Zone — opaque IANA zone or fixed offset, used by EarthCalendar. + * `zone_id` is either an IANA name ("America/New_York", "UTC") or a fixed + * offset string ("+05:30", "-08:00"). The runtime resolves it via tzset() + * on first use of the owning EarthCalendar. */ +el_val_t zone(el_val_t id); +el_val_t zone_utc(void); +el_val_t zone_local(void); +el_val_t zone_offset(el_val_t hours, el_val_t minutes); + +/* Calendar constructors. Each returns an el_val_t pointer to a heap- + * allocated, magic-tagged Calendar struct. Calendars are interned by + * (kind, zone_id, period_ns, epoch_ns) so identical constructors return + * the same pointer — equality is reference equality. */ +el_val_t earth_calendar(el_val_t z); +el_val_t earth_calendar_default(void); +el_val_t mars_calendar(void); +el_val_t cycle_calendar(el_val_t period_dur); +el_val_t no_cycle_calendar(void); +el_val_t relative_calendar(el_val_t epoch_inst); + +/* CalendarTime constructors and methods. Returns a heap-allocated struct + * whose pointer fits in el_val_t. */ +el_val_t now_in(el_val_t cal); +el_val_t in_calendar(el_val_t inst, el_val_t cal); +el_val_t cal_format(el_val_t ct, el_val_t pattern); +el_val_t cal_to_instant(el_val_t ct); +el_val_t cal_cycle_phase(el_val_t ct); +el_val_t cal_in(el_val_t ct, el_val_t cal); + +/* LocalDate / LocalTime / LocalDateTime — calendar-agnostic value types. + * LocalTime carries nanoseconds since midnight as a signed int64 directly + * in the el_val_t slot (no allocation). LocalDate / LocalDateTime are + * heap-allocated structs with magic headers. */ +el_val_t local_date(el_val_t y, el_val_t m, el_val_t d); +el_val_t local_time(el_val_t h, el_val_t m, el_val_t s, el_val_t ns); +el_val_t local_datetime(el_val_t date, el_val_t time); +el_val_t zoned(el_val_t date, el_val_t time, el_val_t cal); + +el_val_t local_date_year(el_val_t ld); +el_val_t local_date_month(el_val_t ld); +el_val_t local_date_day(el_val_t ld); +el_val_t local_time_hour(el_val_t lt); +el_val_t local_time_minute(el_val_t lt); +el_val_t local_time_second(el_val_t lt); +el_val_t local_time_nanos(el_val_t lt); + +el_val_t el_local_date_add_dur(el_val_t ld, el_val_t dur); +el_val_t el_local_time_add_dur(el_val_t lt, el_val_t dur); +el_val_t el_local_date_lt(el_val_t a, el_val_t b); +el_val_t el_local_date_eq(el_val_t a, el_val_t b); + +/* Rhythm — pluggable recurrence AST. Returns a heap-allocated struct + * pointer in el_val_t; rhythms are immutable so callers may share them. */ +el_val_t rhythm_cycle_start(void); +el_val_t rhythm_cycle_phase(el_val_t phase); +el_val_t rhythm_duration(el_val_t d); +el_val_t rhythm_session_start(void); +el_val_t rhythm_event(el_val_t name); +el_val_t rhythm_and(el_val_t a, el_val_t b); +el_val_t rhythm_or(el_val_t a, el_val_t b); +el_val_t rhythm_weekday(el_val_t day); +el_val_t rhythm_weekly_at(el_val_t day, el_val_t hour, el_val_t minute); +el_val_t rhythm_next_after(el_val_t r, el_val_t after, el_val_t cal); +el_val_t rhythm_matches(el_val_t r, el_val_t ct); + /* ── UUID ────────────────────────────────────────────────────────────────── */ el_val_t uuid_new(void); @@ -288,10 +441,53 @@ el_val_t str_char_at(el_val_t s, el_val_t i); el_val_t str_char_code(el_val_t s, el_val_t i); el_val_t str_pad_left(el_val_t s, el_val_t width, el_val_t pad); el_val_t str_pad_right(el_val_t s, el_val_t width, el_val_t pad); -el_val_t str_format(el_val_t template, el_val_t data); +el_val_t str_format(el_val_t fmt, el_val_t data); el_val_t str_lower(el_val_t s); el_val_t str_upper(el_val_t s); +/* ── Text-processing primitives (Phase 1: byte/codepoint, ASCII char classes) + * Phase 2 (filed): Unicode-grapheme awareness, NFC/NFD normalization, regex. + * is_* predicates: empty input returns false; multi-char requires ALL bytes + * to match. ASCII ranges only in Phase 1. */ + +/* Counting */ +el_val_t str_count(el_val_t s, el_val_t sub); /* non-overlapping */ +el_val_t str_count_chars(el_val_t s); /* codepoint count */ +el_val_t str_count_bytes(el_val_t s); /* alias of str_len */ +el_val_t str_count_lines(el_val_t s); +el_val_t str_count_words(el_val_t s); +el_val_t str_count_letters(el_val_t s); /* ASCII [A-Za-z] */ +el_val_t str_count_digits(el_val_t s); /* ASCII [0-9] */ + +/* Find / position */ +el_val_t str_index_of_all(el_val_t s, el_val_t sub); /* [Int] of byte offsets */ +el_val_t str_last_index_of(el_val_t s, el_val_t sub); +el_val_t str_find_chars(el_val_t s, el_val_t any_of); /* first idx of any ch */ + +/* Transform */ +el_val_t str_repeat(el_val_t s, el_val_t n); +el_val_t str_reverse(el_val_t s); /* by codepoint */ +el_val_t str_strip_prefix(el_val_t s, el_val_t prefix); +el_val_t str_strip_suffix(el_val_t s, el_val_t suffix); +el_val_t str_strip_chars(el_val_t s, el_val_t chars); +el_val_t str_lstrip(el_val_t s); +el_val_t str_rstrip(el_val_t s); + +/* Char classification (Bool) */ +el_val_t is_letter(el_val_t s); +el_val_t is_digit(el_val_t s); +el_val_t is_alphanumeric(el_val_t s); +el_val_t is_whitespace(el_val_t s); +el_val_t is_punctuation(el_val_t s); +el_val_t is_uppercase(el_val_t s); +el_val_t is_lowercase(el_val_t s); + +/* Split / join */ +el_val_t str_split_lines(el_val_t s); +el_val_t str_split_chars(el_val_t s); /* alias of native_string_chars */ +el_val_t str_split_n(el_val_t s, el_val_t sep, el_val_t n); +el_val_t str_join(el_val_t list, el_val_t sep); /* alias of list_join */ + /* ── List additions ──────────────────────────────────────────────────────── */ el_val_t list_push(el_val_t list, el_val_t elem); @@ -364,6 +560,19 @@ 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); +/* Layered consciousness — see el_runtime.c for the layered architecture + * design notes (search "Layered consciousness architecture"). The five + * canonical layers (safety / core-identity / domain-knowledge / imprint / + * suit) are seeded automatically; engram_add_layer extends the registry + * with imprint or suit overlays at runtime. Nodes default to layer 1 + * (core-identity) when created via engram_node / engram_node_full. */ +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); +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); @@ -375,6 +584,8 @@ 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); +/* Three-pass activation: background fan-out → working-memory promotion → + * Layer 0 override. See "Three-pass activation" in el_runtime.c. */ 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); @@ -385,9 +596,16 @@ 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); +/* engram_compile_layered_json — produce a prompt-ready text block split + * into "[LAYER 0 — STRUCTURAL]" (non-suppressible layers, sacred fire) + * and "[ENGRAM CONTEXT]" (standard suppressible layers). Returns "" if + * no nodes promoted to working memory. */ +el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth); /* ── LLM (Anthropic API client) ───────────────────────────────────────────── * All functions call https://api.anthropic.com/v1/messages with the API key @@ -476,6 +694,21 @@ el_val_t pq_hybrid_handshake(el_val_t remote_pub_combined); el_val_t sha3_256_hex(el_val_t input); +/* ── AEAD: AES-256-GCM (libcrypto-backed) ─────────────────────────────────── + * Symmetric authenticated encryption used to wrap envelopes after a KEM + * handshake. Caller MUST supply a 32-byte key (64 hex chars) — typically the + * Kyber-768 / hybrid shared_secret, optionally normalized via SHA3-256. + * + * aead_encrypt returns a JSON map {"nonce":"...","ciphertext":"..."} where + * ciphertext is the AES-256-GCM output with the 16-byte auth tag appended. + * Nonce is a fresh 12-byte CSPRNG draw — callers never pick the nonce, which + * structurally rules out the GCM nonce-reuse footgun. + * + * aead_decrypt returns the plaintext String, or "" on any failure (including + * auth-tag mismatch). Callers MUST check for "" before trusting the result. */ +el_val_t aead_encrypt(el_val_t key_hex, el_val_t plaintext); +el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_hex); + /* ── Native VM builtin aliases (for compiled El source) ───────────────────── * These match the El VM's native_* builtins so that El source compiled * to C can call the same names without modification. */ @@ -502,6 +735,22 @@ el_val_t get(el_val_t list, el_val_t index); /* el_list_get */ el_val_t map_get(el_val_t map, el_val_t key); /* el_map_get */ el_val_t map_set(el_val_t map, el_val_t key, el_val_t value); /* el_map_set */ +/* ── OTLP/HTTP Observability ─────────────────────────────────────────────── */ +/* See bottom of el_runtime.c for the implementation. + * Configured by env vars OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_SERVICE_VERSION. + * No-op when OTLP_ENDPOINT is unset. Drop-on-failure semantics. */ +/* ── Subprocess execution ────────────────────────────────────────────────── */ +el_val_t exec_command(el_val_t cmd); /* run shell command, return exit code */ +el_val_t exec_capture(el_val_t cmd); /* run shell command, capture stdout */ +el_val_t exec(el_val_t cmd); /* exec(cmd) → stdout String (30s timeout) */ +el_val_t exec_bg(el_val_t cmd); /* exec_bg(cmd) → PID String (non-blocking) */ + +el_val_t emit_log(el_val_t level, el_val_t msg, el_val_t fields_json); +el_val_t emit_metric(el_val_t name, el_val_t value, el_val_t tags_json); +el_val_t trace_span_start(el_val_t name); +el_val_t trace_span_end(el_val_t span_handle); +el_val_t emit_event(el_val_t name, el_val_t duration_ms); + #ifdef __cplusplus } #endif